Hàm JavaScript tương tự như phạm vi Python ()


96

Có một chức năng nào trong JavaScript tương tự như trong Python range()không?

Tôi nghĩ nên có một cách tốt hơn là mỗi lần viết những dòng sau:

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}

1
@clwen: Rất tiếc là không có, nhưng hãy xem mã của tôi - tôi đã viết một hàm nhằm mô phỏng cách thức range()hoạt động trong Python, vì vậy bạn có thể sử dụng nó. Không có chức năng này trong JavaScript, nhưng có một số plugin cho các khung công tác khác nhau, chẳng hạn như Rangelớp cho MooTools .
Tadeck

Câu trả lời:


89

Không , không có, nhưng bạn có thể làm một cái .

Việc triển khai JavaScript của Python range()

Cố gắng mô phỏng cách nó hoạt động trong Python , tôi sẽ tạo hàm tương tự như sau:

function range(start, stop, step) {
    if (typeof stop == 'undefined') {
        // one param defined
        stop = start;
        start = 0;
    }

    if (typeof step == 'undefined') {
        step = 1;
    }

    if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
        return [];
    }

    var result = [];
    for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
        result.push(i);
    }

    return result;
};

Xem jsfiddle này để biết bằng chứng.

So sánh giữa range()JavaScript và Python

Nó hoạt động theo cách sau:

  • range(4)trả lại [0, 1, 2, 3],
  • range(3,6)trả lại [3, 4, 5],
  • range(0,10,2)trả lại [0, 2, 4, 6, 8],
  • range(10,0,-1)trả lại [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
  • range(8,2,-2)trả lại [8, 6, 4],
  • range(8,2)trả lại [],
  • range(8,2,2)trả lại [],
  • range(1,5,-1)trả lại [],
  • range(1,5,-2)trả lại [],

và đối tác Python của nó hoạt động theo cùng một cách (ít nhất là trong các trường hợp đã đề cập):

>>> range(4)
[0, 1, 2, 3]
>>> range(3,6)
[3, 4, 5]
>>> range(0,10,2)
[0, 2, 4, 6, 8]
>>> range(10,0,-1)
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> range(8,2,-2)
[8, 6, 4]
>>> range(8,2)
[]
>>> range(8,2,2)
[]
>>> range(1,5,-1)
[]
>>> range(1,5,-2)
[]

Vì vậy, nếu bạn cần một hàm hoạt động tương tự như Python range(), bạn có thể sử dụng giải pháp đã đề cập ở trên.


4
có thể là một vài lần kiểm tra phòng thủ bổ sung - đảm bảo rằng các đối số được chuyển qua đều có thể cưỡng chế thành số và đảm bảo rằng stoplớn hơn start(và hoán đổi chúng nếu không).
Russ Cam

1
@RussCam: Cảm ơn bạn đã chỉ ra điều này. Tôi đã không thêm kiểm tra phòng thủ cho các loại, v.v., nhưng tôi đã triển khai thứ tự ngược lại của các phần tử - nó hiện hoạt động giống hệt như đối tác Python, khi tham số cuối cùng là số nguyên âm.
Tadeck

@RussCam: start >= stopdẫn đến một mảng trống là cần thiết nếu mục tiêu thực sự là mô phỏng phạm vi của Python. Và tôi cho rằng nó trực quan hơn dù sao.

1
@delnan: Kiểm tra có thể phức tạp hơn, vì đơn giản start >= stoplà không đủ để hàm này hoạt động như range()trong Python. Tôi đã cập nhật câu trả lời của mình.
Tadeck

@delnan - Tôi không quen với việc triển khai Python. Tôi đoán nếu nó chỉ sẽ được sử dụng bởi peeps quen thuộc với việc thực hiện Python rằng nó làm cho tinh thần để bắt chước nó :)
Russ Cam

125

Đối với một phạm vi rất đơn giản trong ES6:

let range = n => Array.from(Array(n).keys())

Từ bình luận của bigOmega , điều này có thể được rút ngắn bằng cú pháp Spread :

let range = n => [...Array(n).keys()]

41
Một phiên bản đơn giản của việc này làlet range = n => [...Array(n).keys()]
bigOmega

2
@BharathRaja tuyệt vời, cảm ơn! Tại sao không phải là const? Tôi sẽ cố gắng sử dụng const bất cứ nơi nào tôi có thể :) như đối tác chính thức Java;)
Kjellski

Bạn nói đúng, tôi cố gắng sử dụng constcàng nhiều càng tốt, nhưng ở đây nó sẽ chỉ là một tham chiếu đến mảng, vì vậy mảng sẽ vẫn có thể chỉnh sửa: 'D
bigOmega

1
@bigOmega Cái constsẽ áp dụng cho chính hàm, không phải giá trị trả về của nó. Bạn có thể không muốn sửa đổi chức năng theo bất kỳ cách nào.
Solomon Ucko

7
Câu trả lời này không tính đến chỉ số bắt đầu và khả năng tăng kích thước bước.
Fluous

29

2018: câu trả lời này tiếp tục nhận được sự ủng hộ, vì vậy đây là bản cập nhật. Mã bên dưới đã lỗi thời, nhưng may mắn thay, các trình tạo và yieldtừ khóa được chuẩn hóa ES6 , và chúng được hỗ trợ phổ biến trên các nền tảng. Ví dụ về lười range()sử dụng yieldcó thể được tìm thấy ở đây .


Ngoài những gì đã nói, Javascript 1.7+ cung cấp hỗ trợ cho các trình lặp và trình tạo có thể được sử dụng để tạo phiên bản lười biếng, tiết kiệm bộ nhớ range, xrangemô phỏng trong Python2:

function range(low, high) {  
    return {
        __iterator__: function() {
            return {  
                next: function() {
                    if (low > high)
                        throw StopIteration;  
                    return low++;
                }
            }
        }
    }
}

for (var i in range(3, 5))  
  console.log(i); // 3,4,5

1
+1 Ý tưởng tuyệt vời! Bạn cũng có thể triển khai stepđối số và kiểm tra nó trên các giá trị từ câu trả lời của tôi không? Câu trả lời của bạn là tuyệt vời cho các ứng dụng mà chúng tôi có các trình duyệt rất cụ thể (nó sẽ không hoạt động trong Google Chrome, Safari và IE phiên bản cũ hơn 9: stackoverflow.com/a/2209743/548696 ).
Tadeck

@Tadeck: trớ trêu thay, gần đây tôi đã hỏi một câu hỏi tương tự , hãy kiểm tra xem - một số câu trả lời hay ở đó. BTW, mã của bạn không vượt qua bài kiểm tra của tôi; (
georg

Bạn có thể chia sẻ dữ liệu thử nghiệm và kết quả mong đợi? Tôi rất vui khi cải thiện nó, nhưng các bài kiểm tra của tôi đều đậu 100%. Bạn có nói rằng mã tôi đã cung cấp không được phân tích cú pháp chính xác bởi tập lệnh mà bạn đã đặt trong câu hỏi này: stackoverflow.com/q/12173856/548696 ?
Tadeck

@Tadeck: đừng bận tâm. Tôi đã thử nghiệm các lát và mã của bạn dành cho phạm vi.
georg

1
Python rangecó giá trị "cuối cùng" bị loại trừ (vì vậy người ta cần sử dụng >=thay vì >trong mã trên)
Pac0

23

Kết hợp cả hai câu trả lời từ @Tadeck@georg lại với nhau , tôi đã nghĩ ra điều này:

function* range(start, stop, step = 1) {
    if (stop == null) {
        // one param defined
        stop = start;
        start = 0;
    }

    for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
        yield i;
    }
}

Để sử dụng nó trong vòng lặp for, bạn cần vòng lặp for-of-ES6 / JS1.7:

for (let i of range(5)) {
    console.log(i);
}
// Outputs => 0 1 2 3 4

for (let i of range(0, 10, 2)) {
    console.log(i);
}
// Outputs => 0 2 4 6 8

for (let i of range(10, 0, -2)) {
    console.log(i);
}
// Outputs => 10 8 6 4 2

Tại sao không sử dụng các tham số mặc định nếu bạn đang sử dụng ES6?
Amin NAIRI

@Gradiuss sẽ hoạt động cho steptham số, nhưng khi stopkhông được truyền vào, cả hai startstopcần được thay đổi. Tôi sẽ cập nhật nó
janka102

2
WTG, đây là cách triển khai tốt nhất được trình bày cho đến nay. Nó sử dụng bộ tạo (tốt, vì không cần phải lưu toàn bộ chuỗi trong bộ nhớ) và rất thành công.
Lucio Paiva

undefined là một con trỏ đến một đối tượng như null. Có thể so sánh với 3 dấu bằng như: if (stop === undefined) {3 dấu bằng được so sánh mà không cần tự động ép kiểu. so sánh như cũng là loại so sánh. 2 dấu bằng được so sánh với tự động đúc sang loại bên khác.
Shimon Doodkin

20

Một cổng của rangehàm từ Python 2 được cung cấp bởi các thư viện tiện ích underscore.jslodash (cùng với nhiều công cụ hữu ích khác). Ví dụ được sao chép từ tài liệu gạch dưới:

_.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0);
=> []

15

Có thể đạt được bằng cách gắn một trình lặp vào Numbernguyên mẫu

  Number.prototype[Symbol.iterator] = function* () { 
     for (var i = 0; i <= this; i++) {
       yield i
     } 
  }

[...5] // will result in [0,1,2,3,4,5]

Lấy từ khóa học Kyle Simpson's Rethinking Asynchronous JavaScript


7
Đây là thực sự mát mẻ, nhưng nguyên mẫu trọng chỉ là quá mong manh: '(
m0meni

8

Đây là phần mở rộng nhỏ cho một trong các câu trả lời trong trường hợp bạn cần chỉ định cả vị trí bắt đầu và kết thúc của dải ô:

let range = (start, end) => Array.from(Array(end + 1).keys()).slice(start);

7

Của bạn đây.

Thao tác này sẽ ghi (hoặc ghi đè) giá trị của mỗi chỉ mục bằng số chỉ mục.

Array.prototype.writeIndices = function( n ) {
    for( var i = 0; i < (n || this.length); ++i ) this[i] = i;
    return this;
};

Nếu bạn không cung cấp một số, nó sẽ sử dụng độ dài hiện tại của Mảng.

Sử dụng nó như thế này:

var array = [].writeIndices(10);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

5

Để có được một mảng kích thước x, đây là một lớp lót mà không cần sử dụng bất kỳ thư viện nào

var range = n => Array(n + 1).join(1).split('').map((x, i) => i)

hoạt động như

> range(4)
[0, 1, 2, 3]

2
var range = n => Array(n).fill().map((e, i) => i);
Valen

và tại sao nói 'kích thước x' khi bạn thực sự sử dụng nnhư tên tham số
Valen

5

Sau đây là sự điều chỉnh tự nhiên của hàm range () của Python sang JavaScript:

// Generate range from start (inclusive) to stop (exclusive):
function* range(start, stop, step = 1) {
   if (stop === undefined) [start, stop] = [0, start];
   if (step > 0) while (start < stop) yield start, start += step;
   else if (step < 0) while (start > stop) yield start, start += step;
   else throw new RangeError('range() step argument invalid');
} 

// Examples:
console.log([...range(3)]);       // [0, 1, 2]
console.log([...range(0, 3)]);    // [0, 1, 2]
console.log([...range(0, 3, -1)]);// []
console.log([...range(0, 0)]);    // []
console.log([...range(-3)]);      // []
console.log([...range(-3, 0)]);   // [-3, -2, -1]

Nó hỗ trợ bất kỳ đối số nào có thể được so sánh với 0stopvà có thể được tăng dần bởi step. Nó hoạt động giống với phiên bản Python khi được sử dụng với số lượng không vượt quá Number.MAX_SAFE_INTEGER.

Xin lưu ý các trường hợp góc sau:

[...range(0, 0, 0)];        // RangeError: range() step argument invalid
[...range(Number.MAX_SAFE_INTEGER + 1, Number.MAX_SAFE_INTEGER + 2)];  // []
[...range(Number.MAX_SAFE_INTEGER + 2, Number.MAX_SAFE_INTEGER + 3)];  // Infinite loop
[...range(0.7, 0.8, 0.1)];  // [0.7, 0.7999999999999999]
[...range('1', '11')];      // ['1']
[...range('2', '22')];      // Infinite loop

Ngược lại với @ Tadeck của , @ của Volv@ janka102 của câu trả lời mà quay trở lại [], undefinedhoặc nhập một vòng lặp vô hạn khi stepđánh giá lại để 0hay NaN, chức năng máy phát điện này ném một ngoại lệ tương tự như hành vi của Python.


Đã đồng ý. Mặc dù các câu trả lời khác rất thanh lịch theo cách riêng của chúng, nhưng cách tiếp cận và chức năng này khó hiểu hơn nhiều.
Travis Clarke

4

Tinh chỉnh thêm với các thông số mặc định của ES6.

let range = function*(start = 0, stop, step = 1) {
  let cur = (stop === undefined) ? 0 : start;
  let max = (stop === undefined) ? start : stop;
  for (let i = cur; step < 0 ? i > max : i < max; i += step)
    yield i
}

Đây là phạm vi thực sự nên trông như thế nào.
Konrad Linkowski

4

pythonicbắt chước rangehành vi Python tốt nhất mà nó có thể sử dụng trình tạo JS '( yield), hỗ trợ cả trường hợp sử dụng range(stop)range(start, stop, step)trường hợp. Bên cạnh đó, pythonic's rangehàm trả về một Iteratorđối tượng tương tự như Python hỗ trợ mapfilter, vì vậy người ta có thể làm ưa thích một lớp lót như:

import {range} from 'pythonic';
// ...
const results = range(5).map(wouldBeInvokedFiveTimes);
// `results` is now an array containing elements from
// 5 calls to wouldBeInvokedFiveTimes

Cài đặt bằng npm:

npm install --save pythonic

Tiết lộ tôi là tác giả và người duy trì Pythonic


3

MDN khuyến nghị cách tiếp cận này: Trình tạo chuỗi (phạm vi)

// Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));

// Generate numbers range 0..4
console.log("range(0, 4, 1):", range(0, 4, 1));
// [0, 1, 2, 3, 4] 

// Generate numbers range 1..10 with step of 2 
console.log("\nrange(1, 10, 2):", range(1, 10, 2));
// [1, 3, 5, 7, 9]

// Generate the alphabet using Array.from making use of it being ordered as a sequence
console.log("\nrange('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x))", range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x)));
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]


2

Bạn có thể sử dụng thư viện gạch dưới . Nó chứa hàng tá hàm hữu ích để làm việc với mảng và nhiều hàm khác.


2

Có một hàm trong JavaScript tương tự như phạm vi () của Python không?

Tất cả các giải pháp ở đây đều đề cập đến phạm vi của Python 2 (có thể do ví dụ mã bạn đã đưa ra). Tuy nhiên trong Python 3, phương thức range () trả về một trình lặp. JavaScript cũng có các trình vòng lặp và chúng hiệu quả hơn về không gian so với việc tạo toàn bộ mảng và lưu trữ nó trong bộ nhớ.

Vì vậy, các đại diện chính xác hơn về Python 3 của range(n)chức năng là Array(n).keys().

Ví dụ:

for (let i of Array(n).keys()) {
  console.log(i) // 0, 1, 2, 3, ..., n
}

Một ví dụ khác (đã được đề cập trong các câu trả lời khác). Chuyển đổi trình lặp thành một mảng (ES6):

let ary = [...Array(n).keys()];
// ary = [0, 1, 2, 3, ..., n]

1

Vẫn không có chức năng tích hợp nào tương đương range(), nhưng với phiên bản mới nhất - ES2015 - bạn có thể xây dựng triển khai của riêng mình. Đây là một phiên bản giới hạn của nó. Hạn chế vì nó không tính đến tham số bước. Chỉ tối thiểu, tối đa.

const range = (min = null, max = null) =>
  Array.from({length:max ? max - min : min}, (v,k) => max ? k + min : k)

Điều này được thực hiện bằng Array.fromphương pháp có thể xây dựng một mảng từ bất kỳ đối tượng nào có thuộc lengthtính. Vì vậy, việc truyền vào một đối tượng đơn giản chỉ với thuộc lengthtính sẽ tạo ra một ArrayIterator sẽ mang lại lengthsố lượng đối tượng.


1

Đây là cách ưa thích của tôi. Nó cho phép bạn chỉ định một hoặc hai đầu vào như trong Python.

function range(start, end) {
  return Array.from(Array(end||start).keys()).slice(!!end*start)
}

0

Đây là một es6triển khai khác của phạm vi

// range :: (from, to, step?) -> [Number]
const range = (from, to, step = 1) => {
  //swap values if necesery
  [from, to] = from > to ? [to, from] : [from, to]
  //create range array
  return [...Array(Math.round((to - from) / step))]
    .map((_, index) => {
      const negative = from < 0 ? Math.abs(from) : 0
      return index < negative ? 
        from + index * step  :
        (index - negative + 1) * step
    })
}  

range(-20, 0, 5)
  .forEach(val => console.log(val))

for(const val of range(5, 1)){
   console.log(`value ${val}`)
}


Điều gì sẽ xảy ra nếu chúng ta muốn tạo một mảng từ -20to -30?
Amin NAIRI

Rất tiếc, cái này sẽ tạo ra một mảng từ -30 đến -20, nhưng nó có thể dễ dàng sửa đổi để bao gồm thuộc tính đảo ngược nếu bạn muốn. Tôi sẽ sửa câu trả lời ở trên bao gồm nó
mrFunkyWisdom

0

Không, không có, nhưng bạn có thể làm một cái.

Tôi là một phần của hành vi Python3 của phạm vi. Bạn sẽ tìm thấy bên dưới việc triển khai phạm vi của Python () của JavaScript:

function* range(start=0, end=undefined, step=1) {    
    if(arguments.length === 1) {end = start, start = 0}    
    
    [...arguments].forEach(arg => {    
        if( typeof arg !== 'number') {throw new TypeError("Invalid argument")}                               
    })    
    if(arguments.length === 0) {throw new TypeError("More arguments neede")}    
        
    if(start >= end) return                                                                                                                                     
    yield start    
    yield* range(start + step, end, step)    
}    
         
// Use Cases
console.log([...range(5)])

console.log([...range(2, 5)])

console.log([...range(2, 5, 2)])
console.log([...range(2,3)])
// You can, of course, iterate through the range instance.


0

Giả sử bạn cần một phạm vi đơn giản với một bước duy nhất:

let range = (start, end)=> {
    if(start === end) return [start];
    return [start, ...range(start + 1, end)];
}

khác

let range = (start, end, step)=> {
    if(start === end) return [start];
    return [start, ...range(start + step, end)];
}

tham khảo thêm tại đây .


0

Có một hàm trong JavaScript tương tự như phạm vi () của Python không?

Như đã trả lời trước đây: không , không có. Nhưng bạn có thể tự làm. Tôi tin rằng đây là một cách tiếp cận thú vị cho ES6. Nó hoạt động rất giống với Python 2.7 range(), nhưng năng động hơn nhiều.

function range(start, stop, step = 1) 
{
    // This will make the function behave as range(stop)
    if(arguments.length === 1)
    {
        return [...Array(arguments[0]).keys()]
    }

    // Adjusts step to go towards the stop value
    if((start > stop && !(step < 0)) ||
       (start < stop && !(step > 0)))
    {
        step *= -1
    }

    let returnArray = []
    // Checks if i is in the interval between start and stop no matter if stop
    // is lower than start or vice-versa
    for(let i = start; (i-start)*(i-stop) <= 0; i += step)
    {
        returnArray.push(i)
    }
    return returnArray
}

Hàm này có thể hoạt động theo ba cách khác nhau (giống như phạm vi ()) của Python:

  1. range(stop)
  2. range(start, stop)
  3. range(start, stop, step)

Những ví dụ sau:

console.log(range(5))
console.log(range(-2, 2))
console.log(range(2, -2))
console.log(range(10, 20, 2))

Sẽ cung cấp cho bạn kết quả sau:

[ 0, 1, 2, 3, 4 ]
[ -2, -1, 0, 1, 2 ]
[ 2, 1, 0, -1, -2 ]
[ 10, 12, 14, 16, 18, 20 ]

Lưu ý rằng thay vì lặp lại mảng bằng intoán tử (như python), bạn phải sử dụng of. Do đó, ibiến giả định giá trị chứ không phải chỉ số của phần tử của mảng.

for(let i of range(5))
{
    // do something with i...
}

0

Một tùy chọn cho NodeJs là sử dụng Buffer:

[...Buffer.alloc(5).keys()]
// [ 0, 1, 2, 3, 4 ]

Điều thú vị là bạn có thể lặp lại trực tiếp trên bộ đệm:

Buffer.alloc(5).forEach((_, index) => console.log(index))
// 0
// 1
// 2
// 3
// 4

Bạn không thể làm điều đó với Mảng chưa khởi tạo:

Array(5).forEach((_, index) => console.log(index))
// undefined

Nhưng, những người có suy nghĩ đúng đắn của họ sử dụng Bộ đệm cho mục đích như thế này;)


-1

Đây là cách tôi làm điều đó

let n = 5 
[...Array(n).keys()].map(x=>{console.log(x)})

đầu ra

0
1
2
3
4

Điều này không hỗ trợ các bước.
Mark Stosberg
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.