Yalnızca ES2015'te 0'dan n'ye kadar sayı aralığı nasıl oluşturulur?


121

rangePython ve diğerlerinde mevcut olduğu için JavaScript'teki işlevi her zaman eksik buldum ? ES2015'te sayı aralığı oluşturmanın kısa bir yolu var mı?

DÜZENLEME: Sorum, ECMASCRIPT-5'e değil ES2015'e özgü olduğu için bahsedilen tekrardan farklı. Ayrıca aralığın 0'dan başlamasına ve belirli bir başlangıç ​​numarası olmamasına ihtiyacım var (gerçi bu oradaysa iyi olurdu)


Cevap ES5 ve ES6 için aynı.
loganfsmyth

1
Ancak, ES2015'te üreteçler, yeni dizi yöntemleri vb. Gibi bazı yeni kavramları her zaman kullanabilirsiniz. Bu, görevi başarmanız için size fazladan araç seti sağlar
Aditya Singh

7
Ben @Delapouite bu kadar mükemmel bir yanıt olduğunu düşünüyorum yinelenen soruya bir cevap yorumlarında : [...Array(n).keys()].
jib


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

Yanıtlar:


243

Yayılma işlecini yeni oluşturulmuş bir dizinin anahtarları üzerinde kullanabilirsiniz.

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

veya

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

Array.from()Typescript ile çalışan eğer sözdizimi gereklidir


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

2
Bu, typcript'te çalışmaz çünkü keys (), bir Dizi yerine bir Dizi Yineleyicisi döndürür. Daha evrensel bir yaklaşım için aditya-singh yanıtını inceleyin.
David Domingo

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

2
@DavidGonzalezShannon Typescript'te neden [...Array(n).keys()]çalışmadığını biliyor musunuz ? Diğer JS uygulamalarından kasıtlı bir sapma mı?
Stu Cox

Hey @StuCox, neden olduğuna dair hiçbir fikrim yok ama Array(5).keys().slice() ve dilimleme bir dizi yineleyici yöntemi değil. İşte tiplerlang.org/play/…
David Domingo

98

Ayrıca şunu kullanmanın daha sezgisel bir yolunu buldum Array.from:

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

Şimdi bu rangefonksiyon 0'dan n-1'e kadar olan tüm sayıları döndürecektir.

Aralığın değiştirilmiş bir versiyonu desteklenecek startve endşöyledir:

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

DÜZENLE @ marco6 tarafından önerildiği gibi, kullanım durumunuza uygunsa, bunu statik bir yöntem olarak koyabilirsiniz.

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

ve olarak kullan

Array.range(3, 9)

1
Güzel bir! Neden bununla Array statik arayüzünü genişletmiyoruz? interface ArrayConstructor { range(n: number): number[]; } Array.range = n => Array.from({length: n}, (value, key) => key);Array.range(x)...
Daktilo

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

1
Yerleşiklerin geçersiz kılınması artık javascript'te kötü bir uygulama olarak görülüyor.
jhohlfeld

16

Delta ile

JavaScript için

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

Typescript için

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

Güncelleme

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

Düzenle

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

Güncellenmiş TypeScript sürümünüz çalışmıyor. Belirtilen boyutta boş bir dizi oluşturur. TypeScript ile Array.keys ile Array.from kullanmanız gerekir. Array.from(Array(~~((to - from) / step) + 1).keys())
David Domingo

13

0 ile 5 arasındaki sayılar için

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

10

Bu çözümlerin çoğu, birçok durumda işi tamamlayabilen ancak range(Infinity). Bu sorunları önlemek ve sonsuz dizileri desteklemek için basit bir jeneratör kullanabilirsiniz:

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

Örnekler:

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

Yani bu durumda, Number nesnesinin yayılma operatörüyle bir Array nesnesi gibi davranması iyi olurdu.

Örneğin , yayma operatörüyle kullanılan Array nesnesi:

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

Bu şekilde çalışır çünkü Array nesnesi yerleşik bir yineleyiciye sahiptir.
Bizim durumumuzda, benzer bir işleve sahip olmak için bir Number nesnesine ihtiyacımız var:

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

Bunu yapmak için, bu amaç için basitçe Sayı yineleyici oluşturabiliriz.

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

Artık yayılma operatörüyle 0 ile N arasında aralıklar oluşturmak mümkündür.

[... N] // artık 0 ... N dizisi döndürüyor

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

Şerefe.


3

Aralığı yalnızca gerektiğinde tembel olarak oluşturan bir jeneratör işlevi kullanabilirsiniz:

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

Oluşturucu üzerinde eşleme yapmak için daha yüksek bir sıra oluşturucu işlevi kullanabilirsiniz range:

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

Korkusuzsanız, çok daha geniş bir yelpazeyi ele almak için jeneratör yaklaşımını genelleştirebilirsiniz (amaçlanan):

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

Üreticilerin / yineleyicilerin doğası gereği durum bilgili olduğunu, yani her çağrılmasında örtük bir durum değişikliği olduğunu unutmayın next. Devlet karışık bir nimettir.


3

Python'a benzer şekilde çalışan ES6 adımıyla aralık 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)
}

Örnekler:

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
Soruya güzel bir ek! Bu, Angular 8 html * ngFor döngü şablonlarımda çok daha temiz kod elde etmeme yardımcı oldu.
Sam

2

Deltayı desteklemek için

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

1

Bunu, aşağıdaki gibi adım destekli tek bir astarla da yapabilirsiniz:

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

Sonuç [0, 1, 2, 3, 4, 5, 6 ,7 ,8 ,9].


2
Bu Y-birleştirici mi?
TheChetan

1
Y-birleştirici fikrini takip eder.
Marcin Król

1

Bu işlev bir tamsayı dizisi döndürür.

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

Typescript'te


Her ikisini de kullanmak Array.fromve sözdizimini yaymak için hiçbir neden yok . Ve sonra mevcut cevapla tamamen aynı.
Bergi

Sadece [...Array(n).keys()]belirtmek istiyorum , Typescript'te çalışmıyor.
PeiSong

3
O zaman kullan Array.from(Array(n).keys()). Yine de çalışması gerektiğinden oldukça eminim, yayılma sözdizimi ile literal neye aktarılır?
Bergi

0

İşte kullanmayan başka bir varyasyon Array.

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

0

Üreteçler artık sayı dizisini tembel olarak oluşturmanıza ve büyük aralıklar için daha az bellek kullanmanıza izin veriyor.

Soru özellikle ES2015'i belirtirken, birçok Typescript kullanıcısının burada sona ereceğini ve ES'ye dönüşümün basit olmasını bekliyorum ...

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

İlk iki işlev bildirimi, IDE'nizde daha bilgilendirici tamamlama önerileri sağlamak içindir.


Aaaa ve göndermeden önce mevcut tüm cevapları okumadığımı söyleyebilirsin: - /
Dave

0

Sadece haritalamaya ne dersin ....

Array (n) .map ((değer, dizin) ....) oradaki yolun% 80'idir. Ancak bazı garip nedenlerden dolayı işe yaramıyor. Ancak bir çözüm var.

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

Bir aralık için

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

Garip, bu iki yineleyici aynı sonucu döndürür: Array(end-start+1).entries()veArray(end-start+1).fill().entries()

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.