Bagaimana cara menghasilkan rentang angka dari 0 hingga n di ES2015 saja?


122

Saya selalu menemukan rangefungsi yang hilang dari JavaScript karena tersedia di python dan lainnya? Apakah ada cara ringkas untuk menghasilkan rentang angka di ES2015?

EDIT: Pertanyaan SAYA berbeda dari duplikat yang disebutkan karena khusus untuk ES2015 dan bukan ECMASCRIPT-5. Saya juga membutuhkan kisaran untuk mulai dari 0 dan bukan nomor awal yang spesifik (meskipun akan lebih baik jika itu ada)


Jawabannya sama untuk ES5 dan ES6.
loganfsmyth

1
Tetapi Anda selalu dapat menggunakan beberapa konsep baru seperti generator, metode array baru, dll. Di ES2015. Itu memberi Anda seperangkat alat tambahan untuk mencapai tugas
Aditya Singh

7
Saya pikir @Delapouite memiliki jawaban yang sempurna untuk ini dalam komentar untuk jawaban atas pertanyaan digandakan : [...Array(n).keys()].
jib


2
[...Array(5)].map((_,i) => i+1)
nick indiessance

Jawaban:


243

Anda dapat menggunakan operator sebaran pada kunci dari array yang baru dibuat.

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

atau

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

The Array.from()sintaks diperlukan jika bekerja dengan naskah


38
Manis:function range (start, end) { return [...Array(1+end-start).keys()].map(v => start+v) }
conny

2
Ini tidak berfungsi dalam skrip ketikan karena keys () mengembalikan Iterator Array alih-alih Array. Lihat jawaban aditya-singh untuk pendekatan yang lebih universal.
David Domingo

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

2
@DavidGonzalezShannon Apakah Anda tahu mengapa [...Array(n).keys()]tidak bekerja di Ketikan? Apakah ini penyimpangan yang disengaja dari implementasi JS lainnya?
Stu Cox

Hai @StuCox Saya tidak tahu mengapa tetapi itu mentransformasikannya ke Array(5).keys().slice()dan slice bukan metode iterator array. Berikut adalah contoh tidak berfungsi typescriptlang.org/play/…
David Domingo

97

Saya juga menemukan satu cara yang lebih intuitif menggunakan Array.from:

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

Sekarang rangefungsi ini akan mengembalikan semua angka mulai dari 0 hingga n-1

Versi rentang yang dimodifikasi untuk mendukung startdan endadalah:

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

EDIT Seperti yang disarankan oleh @ marco6, Anda dapat meletakkan ini sebagai metode statis jika sesuai dengan kasus penggunaan Anda

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

dan gunakan sebagai

Array.range(3, 9)

1
Bagus! Mengapa kita tidak memperluas antarmuka statis Array dengannya? Dalam ketikan bekerja sangat baik dengan: interface ArrayConstructor { range(n: number): number[]; } Array.range = n => Array.from({length: n}, (value, key) => key); Dan kemudian di mana-manaArray.range(x)...
marco6

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

Mengganti bawaan sekarang dianggap sebagai praktik buruk di javascript.
jhohlfeld

16

Dengan Delta

Untuk 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

Untuk Ketikan

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];

Memperbarui

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);

Sunting

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);

Versi TypeScript Anda yang diperbarui tidak berfungsi. Ini membuat array kosong dengan ukuran yang ditunjukkan. Anda perlu menggunakan Array.from dengan Array.keys dengan TypeScript. Array.from(Array(~~((to - from) / step) + 1).keys())
David Domingo

13

Untuk angka 0 sampai 5

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

10

Banyak dari solusi ini dibuat berdasarkan contoh objek Array nyata, yang dapat menyelesaikan pekerjaan untuk banyak kasus tetapi tidak dapat mendukung kasus seperti range(Infinity). Anda dapat menggunakan generator sederhana untuk menghindari masalah ini dan mendukung urutan tak terbatas:

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

Contoh:

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

Jadi, dalam hal ini, alangkah baiknya jika Number objek akan berperilaku seperti objek Array dengan operator spread.

Misalnya objek Array yang digunakan dengan operator spread:

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

Ia bekerja seperti ini karena objek Array memiliki iterator bawaan.
Dalam kasus kami, kami memerlukan objek Angka untuk memiliki fungsi serupa:

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

Untuk melakukan itu kita cukup membuat iterator Angka untuk tujuan itu.

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

Sekarang dimungkinkan untuk membuat rentang dari 0 hingga N dengan operator penyebaran.

[... N] // sekarang mengembalikan 0 ... N array

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

Bersulang.


3

Anda bisa menggunakan fungsi generator, yang membuat rentang dengan malas hanya jika diperlukan:

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()
);

Anda dapat menggunakan fungsi generator tingkat tinggi untuk memetakan rangegenerator:

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]
);

Jika Anda tidak takut, Anda bahkan dapat menggeneralisasi pendekatan generator untuk menangani rentang yang lebih luas (permainan kata-kata):

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]
);

Perlu diingat bahwa generator / iterator secara inheren bersifat stateful, yaitu, ada perubahan status implisit dengan setiap pemanggilan next. Negara adalah berkah campuran.


3

Rentang dengan langkah ES6, yang berfungsi mirip dengan 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)
}

Contoh:

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
Bagus untuk menambah pertanyaan! Ini membantu saya mendapatkan kode yang lebih bersih di template loop Angular 8 html * ngFor saya.
Sam

2

Untuk mendukung delta

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

1

Anda juga dapat melakukannya dengan satu liner dengan dukungan langkah seperti ini:

((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)

Hasilnya adalah [0, 1, 2, 3, 4, 5, 6 ,7 ,8 ,9].


2
Apakah ini kombinator-Y?
TheChetan

1
Ini mengikuti ide kombinator-Y.
Marcin Król

1

Fungsi ini akan mengembalikan urutan integer.

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);

di Ketikan


Tidak ada alasan untuk menggunakan keduanya Array.fromdan menyebarkan sintaks. Dan kemudian sama persis dengan jawaban yang ada.
Bergi

Hanya ingin menunjukkan [...Array(n).keys()]tidak berfungsi di Ketikan.
PeiSong

3
Kemudian gunakan Array.from(Array(n).keys()). Saya cukup yakin itu harus bekerja, apa yang literal dengan sintaks menyebar transparan ke?
Bergi

0

Berikut variasi lain yang tidak digunakan Array.

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

0

Generator sekarang memungkinkan Anda untuk membuat urutan angka dengan malas dan menggunakan lebih sedikit memori untuk rentang yang besar.

Sementara pertanyaannya secara khusus menyatakan ES2015, saya berharap banyak pengguna Typecript akan berakhir di sini dan konversi ke ES mudah ...

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++;
    }
}

Dua deklarasi fungsi pertama hanya untuk memberikan saran penyelesaian yang lebih informatif di IDE Anda.


Aaaa dan Anda bisa tahu saya tidak membaca semua jawaban yang ada sebelum memposting: - /
Dave

0

Bagaimana kalau hanya memetakan ....

Array (n) .map ((value, index) ....) sudah 80% jalan ke sana. Tetapi untuk beberapa alasan aneh itu tidak berhasil. Tapi ada solusinya.

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

Untuk rentang

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

Aneh, kedua iterator ini mengembalikan hasil yang sama: Array(end-start+1).entries()danArray(end-start+1).fill().entries()

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.