Làm cách nào để tạo dải số từ 0 đến n chỉ trong ES2015?


122

Tôi luôn thấy rangehàm bị thiếu trong JavaScript vì nó có sẵn trong python và các hàm khác? Có cách nào ngắn gọn để tạo dải số trong ES2015 không?

CHỈNH SỬA: Câu hỏi của TÔI khác với bản sao đã đề cập vì nó dành riêng cho ES2015 chứ không phải ECMASCRIPT-5. Ngoài ra, tôi cần phạm vi bắt đầu từ 0 và không phải là số bắt đầu cụ thể (mặc dù sẽ tốt nếu có)


Câu trả lời là giống nhau đối với ES5 và ES6.
loganfsmyth

1
Nhưng bạn luôn có thể sử dụng một số khái niệm mới như trình tạo, phương thức mảng mới, v.v. trong ES2015. Cung cấp cho bạn thêm bộ công cụ để đạt được các nhiệm vụ
Aditya Singh

7
Tôi nghĩ @Delapouite có câu trả lời hoàn hảo để này trong ý kiến để một câu trả lời cho câu hỏi trùng lặp : [...Array(n).keys()].
jib


2
[...Array(5)].map((_,i) => i+1)
nick indessance 26/02/19

Câu trả lời:


243

Bạn có thể sử dụng toán tử spread trên các phím của một mảng mới được tạo.

[...Array(n).keys()]

hoặc là

Array.from(Array(n).keys())

Các Array.from()cú pháp là cần thiết nếu làm việc với nguyên cảo


38
Ngọt ngào:function range (start, end) { return [...Array(1+end-start).keys()].map(v => start+v) }
conny

2
Điều này không hoạt động trong bảng chữ vì các khóa () trả về một Trình lặp mảng thay vì một Mảng. Kiểm tra câu trả lời của aditya-singh để có cách tiếp cận phổ quát hơn.
David Domingo,

3
…… hoặc Array.from(Array(n).keys()).
Константин Ван

2
@DavidGonzalezShannon Bạn có biết tại sao [...Array(n).keys()]không hoạt động trong Typecript không? Nó có phải là sự sai lệch có chủ ý so với các triển khai JS khác không?
Stu Cox,

Này @StuCox Tôi không biết tại sao nhưng nó chuyển nó thành Array(5).keys().slice()và slice không phải là một phương thức của trình lặp mảng. Dưới đây là một ví dụ về nó không làm việc typescriptlang.org/play/...
David Domingo

97

Tôi cũng tìm thấy một cách trực quan hơn bằng cách sử dụng Array.from:

const range = n => Array.from({length: n}, (value, key) => key)

Bây giờ rangehàm này sẽ trả về tất cả các số bắt đầu từ 0 đến n-1

Một phiên bản đã sửa đổi của dải ô để hỗ trợ startendlà:

const range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);

CHỈNH SỬA Theo đề xuất của @ marco6, bạn có thể đặt phương thức này làm phương thức tĩnh nếu nó phù hợp với trường hợp sử dụng của bạn

Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);

và sử dụng nó như

Array.range(3, 9)

1
Đẹp quá! Tại sao chúng ta không mở rộng giao diện tĩnh Array với nó? Trong bản đánh chữ hoạt động tuyệt vời với: interface ArrayConstructor { range(n: number): number[]; } Array.range = n => Array.from({length: n}, (value, key) => key); Và sau đó ở khắp mọi nơiArray.range(x)...
marco6

[ts] Property 'range' does not exist on type 'ArrayConstructor'. thouths?
kuncevic.dev Ngày

Ghi đè các bản cài sẵn được coi là hành vi xấu trong javascript hiện nay.
jhohlfeld

16

Với Delta

Đối với javascript

Array.from(Array(10).keys()).map(i => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

[...Array(10).keys()].map(i => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]

Array(10).fill(0).map((v, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

Array(10).fill().map((v, i) => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]

[...Array(10)].map((v, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

const range = (from, to, step) =>
  Array(~~((to - from) / step) + 1) // '~~' is Alternative for Math.floor()
  .fill().map((v, i) => from + i * step);

range(0, 9, 2);
//=> [0, 2, 4, 6, 8]

Array.range = (from, to, step) => Array.from({
    length: ~~((to - from) / step) + 1
  },
  (v, k) => from + k * step
);

Array.range = (from, to, step) => [...Array(~~((to - from) / step) + 1)].map(
  (v, k) => from + k * step
)
Array.range(2, 10, 2);
//=> [2, 4, 6, 8, 10]

Array.range(0, 10, 1);
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Array.range(2, 10, -1);
//=> []

Array.range(3, 0, -1);
//=> [3, 2, 1, 0]


class Range {
  constructor(total = 0, step = 1, from = 0) {
    this[Symbol.iterator] = function*() {
      for (let i = 0; i < total; yield from + i++ * step) {}
    };
  }
}

[...new Range(5)]; // Five Elements
//=> [0, 1, 2, 3, 4]
[...new Range(5, 2)]; // Five Elements With Step 2
//=> [0, 2, 4, 6, 8]
[...new Range(5, -2, 10)]; // Five Elements With Step -2 From 10
//=>[10, 8, 6, 4, 2]
[...new Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]

// Also works with for..of loop
for (i of new Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2

// Or
const Range = function*(total = 0, step = 1, from = 0){
  for (let i = 0; i < total; yield from + i++ * step) {}
};

Array.from(Range(5, -2, -10));
//=> [-10, -12, -14, -16, -18]
[...Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]

// Also works with for..of loop
for (i of Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2

class Range2 {
  constructor(to = 0, step = 1, from = 0) {
    this[Symbol.iterator] = function*() {
      let i = 0,
        length = ~~((to - from) / step) + 1;
      while (i < length) yield from + i++ * step;
    };
  }
}
[...new Range2(5)]; // First 5 Whole Numbers
//=> [0, 1, 2, 3, 4, 5]

[...new Range2(5, 2)]; // From 0 to 5 with step 2
//=> [0, 2, 4]

[...new Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]

// Or 
const Range2 = function*(to = 0, step = 1, from = 0) {
    let i = 0, length = ~~((to - from) / step) + 1;
    while (i < length) yield from + i++ * step;
};


[...Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]

let even4to10 = Range2(10, 2, 4);
even4to10.next().value
//=> 4
even4to10.next().value
//=> 6
even4to10.next().value
//=> 8
even4to10.next().value
//=> 10
even4to10.next().value
//=> undefined

Đối với chữ viết

interface _Iterable extends Iterable < {} > {
  length: number;
}

class _Array < T > extends Array < T > {
  static range(from: number, to: number, step: number): number[] {
    return Array.from(
      ( < _Iterable > { length: Math.floor((to - from) / step) + 1 }),
      (v, k) => from + k * step
    );
  }
}
_Array.range(0, 9, 1);
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

Cập nhật

class _Array<T> extends Array<T> {
    static range(from: number, to: number, step: number): number[] {
        return [...Array(~~((to - from) / step) + 1)].map(
            (v, k) => from + k * step
        );
    }
}
_Array.range(0, 9, 1);

Biên tập

class _Array<T> extends Array<T> {
    static range(from: number, to: number, step: number): number[] {
        return Array.from(Array(~~((to - from) / step) + 1)).map(
            (v, k) => from + k * step
        );
    }
}
_Array.range(0, 9, 1);

Phiên bản TypeScript cập nhật của bạn không hoạt động. Nó tạo ra một mảng trống với kích thước được chỉ định. Bạn cần sử dụng Array.from với Array.keys với TypeScript. Array.from(Array(~~((to - from) / step) + 1).keys())
David Domingo

13

Đối với các số từ 0 đến 5

[...Array(5).keys()];
=> [0, 1, 2, 3, 4]

10

Rất nhiều giải pháp này xây dựng dựa trên việc khởi tạo các đối tượng Array thực, có thể hoàn thành công việc cho nhiều trường hợp nhưng không thể hỗ trợ các trường hợp như range(Infinity). Bạn có thể sử dụng một trình tạo đơn giản để tránh những vấn đề này và hỗ trợ chuỗi vô hạn:

function* range( start, end, step = 1 ){
  if( end === undefined ) [end, start] = [start, 0];
  for( let n = start; n < end; n += step ) yield n;
}

Ví dụ:

Array.from(range(10));     // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array.from(range(10, 20)); // [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]

i = range(10, Infinity);
i.next(); // { value: 10, done: false }
i.next(); // { value: 11, done: false }
i.next(); // { value: 12, done: false }
i.next(); // { value: 13, done: false }
i.next(); // { value: 14, done: false }

8

Vì vậy, trong trường hợp này, sẽ rất tốt nếu Số đối tượng hoạt động giống như một đối tượng Array với toán tử spread.

Ví dụ, đối tượng mảng được sử dụng với toán tử spread:

let foo = [0,1,2,3];
console.log(...foo) // returns 0 1 2 3

Nó hoạt động như vậy vì đối tượng Array có một trình vòng lặp được tích hợp sẵn.
Trong trường hợp của chúng tôi, chúng tôi cần một đối tượng Number để có chức năng tương tự:

[...3] //should return [0,1,2,3]

Để làm điều đó, chúng ta có thể chỉ cần tạo Trình lặp số cho mục đích đó.

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

Bây giờ có thể tạo phạm vi từ 0 đến N bằng toán tử spread.

[... N] // bây giờ trả về 0 ... N mảng

http://jsfiddle.net/01e4xdv5/4/

Chúc mừng.


3

Bạn có thể sử dụng chức năng tạo, chỉ tạo phạm vi một cách lười biếng khi cần:

function* range(x, y) {
  while (true) {
    if (x <= y)
      yield x++;

    else
      return null;
  }
}

const infiniteRange = x =>
  range(x, Infinity);
  
console.log(
  Array.from(range(1, 10)) // [1,2,3,4,5,6,7,8,9,10]
);

console.log(
  infiniteRange(1000000).next()
);

Bạn có thể sử dụng chức năng trình tạo bậc cao hơn để ánh xạ qua trình rangetạo:

function* range(x, y) {
  while (true) {
    if (x <= y)
      yield x++;

    else
      return null;
  }
}

const genMap = f => gx => function* (...args) {
  for (const x of gx(...args))
    yield f(x);
};

const dbl = n => n * 2;

console.log(
  Array.from(
    genMap(dbl) (range) (1, 10)) // [2,4,6,8,10,12,14,16,18,20]
);

Nếu bạn không sợ hãi, bạn thậm chí có thể tổng quát hóa cách tiếp cận trình tạo để giải quyết một phạm vi rộng hơn nhiều (ý định chơi chữ):

const rangeBy = (p, f) => function* rangeBy(x) {
  while (true) {
    if (p(x)) {
      yield x;
      x = f(x);
    }

    else
      return null;
  }
};

const lte = y => x => x <= y;

const inc = n => n + 1;

const dbl = n => n * 2;

console.log(
  Array.from(rangeBy(lte(10), inc) (1)) // [1,2,3,4,5,6,7,8,9,10]
);

console.log(
  Array.from(rangeBy(lte(256), dbl) (2)) // [2,4,8,16,32,64,128,256]
);

Hãy nhớ rằng trình tạo / trình vòng lặp vốn có trạng thái, có một sự thay đổi trạng thái ngầm với mỗi lần gọi next. Nhà nước là một phước lành hỗn hợp.


3

Phạm vi với bước ES6, hoạt động tương tự như python list(range(start, stop[, step])):

const range = (start, stop, step = 1) => {
  return [...Array(stop - start).keys()]
    .filter(i => !(i % Math.round(step)))
    .map(v => start + v)
}

Ví dụ:

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

1
Vui vẻ thêm vào câu hỏi! Điều này đã giúp tôi tìm ra mã rõ ràng hơn nhiều trong các mẫu vòng lặp Angular 8 html * ngFor.
Sam

2

Để hỗ trợ delta

const range = (start, end, delta) => {
  return Array.from(
    {length: (end - start) / delta}, (v, k) => (k * delta) + start
  )
};

1

Bạn cũng có thể làm điều đó với một lớp lót có hỗ trợ bước như thế này:

((from, to, step) => ((add, arr, v) => add(arr, v, add))((arr, v, add) => v < to ? add(arr.concat([v]), v + step, add) : arr, [], from))(0, 10, 1)

Kết quả là [0, 1, 2, 3, 4, 5, 6 ,7 ,8 ,9].


2
Đây có phải là bộ tổ hợp Y không?
TheChetan

1
Nó tuân theo ý tưởng của Y-combinator.
Marcin Król

1

Hàm này sẽ trả về một dãy số nguyên.

const integerRange = (start, end, n = start, arr = []) =>
  (n === end) ? [...arr, n]
    : integerRange(start, end, start < end ? n + 1 : n - 1, [...arr, n]);

$> integerRange(1, 1)
<- Array [ 1 ]

$> integerRange(1, 3)
<- Array(3) [ 1, 2, 3 ]

$> integerRange(3, -3)
<- Array(7) [ 3, 2, 1, 0, -1, -2, -3 ]

0
const keys = Array(n).keys();
[...Array.from(keys)].forEach(callback);

trong Typecript


Không có lý do gì để sử dụng cả hai Array.fromvà cú pháp lây lan. Và sau đó nó giống hệt như câu trả lời hiện có.
Bergi

Chỉ muốn chỉ ra [...Array(n).keys()]không hoạt động trong Typecript.
PeiSong

3
Sau đó sử dụng Array.from(Array(n).keys()). Tôi khá chắc chắn rằng nó sẽ hoạt động, mặc dù vậy, nghĩa đen với cú pháp lây lan có nghĩa là gì?
Bergi

0

Đây là một biến thể khác không sử dụng Array.

let range = (n, l=[], delta=1) => {
  if (n < 0) { 
    return l 
  }
  else {
    l.unshift(n)
    return range(n - delta, l) 
  }
}

0

Giờ đây, các trình tạo cho phép bạn tạo dãy số một cách lười biếng và sử dụng ít bộ nhớ hơn cho các phạm vi lớn.

Mặc dù câu hỏi nói rõ về ES2015, tôi hy vọng sẽ có rất nhiều người dùng Typecript kết thúc ở đây và việc chuyển đổi sang ES rất đơn giản ...

function range(end: number): IterableIterator<number>;
// tslint:disable-next-line:unified-signatures
function range(begin: number, end: number): IterableIterator<number>;

function *range(begin: number, end: number = NaN): IterableIterator<number> {
    let num = 0;
    if (isNaN(end)) {
        end = begin;
    } else {
        num = begin;
    }
    while (num < end) {
        yield num++;
    }
}

Hai khai báo hàm đầu tiên chỉ để cung cấp các đề xuất hoàn thành nhiều thông tin hơn trong IDE của bạn.


Aaaaand bạn có thể nói tôi đã không đọc tất cả các câu trả lời hiện có trước khi gửi bài: - /
Dave

0

Làm thế nào về việc chỉ lập bản đồ ....

Array (n) .map ((value, index) ....) là 80% chặng đường ở đó. Nhưng vì một số lý do kỳ quặc mà nó không hoạt động. Nhưng có một cách giải quyết.

Array(n).map((v,i) => i) // does not work
Array(n).fill().map((v,i) => i) // does dork

Đối với một phạm vi

Array(end-start+1).fill().map((v,i) => i + start) // gives you a range

Thật kỳ lạ, hai trình lặp này trả về cùng một kết quả: Array(end-start+1).entries()Array(end-start+1).fill().entries()

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.