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)

Aditya Singh
sumber
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

Delapouite
sumber
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)
Aditya Singh
sumber
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);
nkitku
sumber
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]
Ben
sumber
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 }
Iron Savior
sumber
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.

Getriax
sumber
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.


sumber
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]
juanesarango
sumber
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
  )
};
pengguna3500066
sumber
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].

Marcin Król
sumber
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 ]
Zack
sumber
0
const keys = Array(n).keys();
[...Array.from(keys)].forEach(callback);

di Ketikan

PeiSong
sumber
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) 
  }
}
Han Lazarus
sumber
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.

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

Arturo Hernandez
sumber