ES2015에서만 0에서 n까지의 숫자 범위를 생성하는 방법은 무엇입니까?


122

range파이썬 및 기타에서 사용할 수 있으므로 항상 JavaScript에서 누락 된 함수를 찾았 습니까? ES2015에서 숫자 범위를 생성하는 간결한 방법이 있습니까?

편집 : 내 질문은 ECMASCRIPT-5가 아닌 ES2015에만 해당하므로 언급 된 중복과 다릅니다. 또한 특정 시작 번호가 아닌 0에서 시작하는 범위가 필요합니다 (그게 있으면 좋을 것입니다)


ES5와 ES6의 대답은 동일합니다.
loganfsmyth

1
그러나 ES2015에서 생성기, 새로운 배열 방법 등과 같은 새로운 개념을 항상 사용할 수 있습니다. 그것은 당신에게 작업을 달성하기위한 도구의 추가 설정 제공
디트 야 싱

7
나는 @Delapouite는이에 대한 완벽한 해답이 있다고 생각 중복 된 질문에 대한 답변에 대한 의견을 : [...Array(n).keys()].
jib


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

답변:


243

새로 생성 된 배열의 키에 스프레드 연산자를 사용할 수 있습니다.

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

또는

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

Array.from()타이프 라이터로 작업하는 경우 구문이 필요하다


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

2
keys ()는 Array 대신 Array Iterator를 반환하기 때문에 typescript에서는 작동하지 않습니다. 보다 보편적 인 접근 방식에 대한 aditya-singh의 답변을 확인하십시오.
David Domingo

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

2
@DavidGonzalezShannon [...Array(n).keys()]Typescript에서 작동하지 않는 이유를 아십니까 ? 다른 JS 구현과의 의도적 인 편차입니까?
Stu Cox

안녕하세요 @StuCox 저는 왜 그런지 모르겠지만 그것을 변환 Array(5).keys().slice()하고 슬라이스는 배열 반복기의 방법이 아닙니다. 다음은 작동하지 않는 예입니다. typescriptlang.org/play/…
데이비드 도밍고

98

또한 Array.from다음을 사용하는 더 직관적 인 방법을 발견했습니다 .

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

이제이 range함수는 0부터 n-1까지 모든 숫자를 반환합니다.

지원할 범위의 수정 된 버전이며 다음 startend같습니다.

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

편집 @ marco6에서 제안한대로 사용 사례에 적합한 경우 정적 방법으로 넣을 수 있습니다.

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

그리고 그것을 사용하십시오

Array.range(3, 9)

1
멋지네요! Array 정적 인터페이스를 확장하지 않는 이유는 무엇입니까? In typescript는 interface ArrayConstructor { range(n: number): number[]; } Array.range = n => Array.from({length: n}, (value, key) => key); 다음 과 잘 작동합니다 : 그리고 모든 곳에서Array.range(x)...
marco6

[ts] Property 'range' does not exist on type 'ArrayConstructor'. 너는?
kuncevic.dev 2008 년

1
내장 기능을 재정의하는 것은 이제 자바 스크립트에서 나쁜 습관으로 간주됩니다.
jhohlfeld

16

델타와 함께

자바 스크립트의 경우

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 용

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

최신 정보

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

편집하다

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

업데이트 된 TypeScript 버전이 작동하지 않습니다. 표시된 크기로 빈 배열을 만듭니다. Array.from을 TypeScript와 함께 Array.keys와 함께 사용해야합니다. Array.from(Array(~~((to - from) / step) + 1).keys())
David Domingo

13

숫자 0 ~ 5

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

10

이러한 솔루션의 대부분은 실제 Array 객체를 인스턴스화하여 빌드하므로 많은 경우에 대한 작업을 수행 할 수 있지만 range(Infinity). 간단한 생성기를 사용하여 이러한 문제를 방지하고 무한 시퀀스를 지원할 수 있습니다.

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

예 :

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

따라서이 경우에는 Number 객체가 스프레드 연산자를 사용하여 Array 객체처럼 동작 것입니다.

예를 들어 스프레드 연산자와 함께 사용되는 Array 객체 :

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

Array 객체에 반복기가 내장되어 있기 때문에 이와 같이 작동합니다.
우리의 경우 비슷한 기능을 가지려면 Number 객체 가 필요 합니다.

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

이를 위해 단순히 그 목적을위한 숫자 반복기를 만들 수 있습니다.

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

이제 확산 연산자를 사용하여 0에서 N까지의 범위를 만들 수 있습니다.

[... N] // 이제 0 ... N 배열 반환

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

건배.


3

필요할 때만 범위를 느리게 생성하는 생성기 함수를 사용할 수 있습니다.

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

고차 생성기 함수를 사용하여 생성기를 매핑 할 수 있습니다 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]
);

두려움이 없다면 훨씬 더 넓은 범위 (의도 된 말장난)를 해결하기 위해 생성기 접근 방식을 일반화 할 수도 있습니다.

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

생성기 / 반복자는 본질적으로 상태 저장입니다. 즉,를 호출 할 때마다 암시 적 상태가 변경됩니다 next. 상태는 혼합 된 축복입니다.


3

python과 유사하게 작동하는 ES6 단계의 범위 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)
}

예 :

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
질문에 좋은 추가! 이것은 Angular 8 html * ngFor 루프 템플릿에서 훨씬 더 깨끗한 코드를 얻는 데 도움이되었습니다.

2

델타를 지원하려면

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

1

다음과 같은 단계 지원이있는 하나의 라이너로도 수행 할 수 있습니다.

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

결과는 [0, 1, 2, 3, 4, 5, 6 ,7 ,8 ,9]입니다.


2
이것이 Y- 콤비 네이터입니까?
TheChetan

1
Y-combinator의 아이디어를 따릅니다.
Marcin Król 2017 년

1

이 함수는 정수 시퀀스를 반환합니다.

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에서


Array.from및 확산 구문을 모두 사용할 이유가 없습니다 . 그리고 그것은 기존의 대답과 똑같습니다.
Bergi

[...Array(n).keys()]Typescript에서 작동하지 않음을 지적하고 싶습니다 .
PeiSong

3
그런 다음 Array.from(Array(n).keys()). 나는 그것이 작동해야한다고 확신합니다. 확산 구문이있는 리터럴은 무엇으로 변환됩니까?
Bergi

0

사용하지 않는 또 다른 변형이 있습니다 Array.

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

0

이제 생성기를 사용하면 숫자 시퀀스를 느리게 생성하고 넓은 범위에 더 적은 메모리를 사용할 수 있습니다.

질문에 ES2015가 구체적으로 명시되어 있지만 많은 Typescript 사용자가 여기에서 끝나고 ES 로의 변환이 간단합니다 ...

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

처음 두 함수 선언은 IDE에서보다 유익한 완성 제안을 제공하기위한 것입니다.


Aaaaand 당신은 내가 게시하기 전에 모든 기존 답변을 읽지 않았다고 말할 수 있습니다 :-/
Dave

0

그냥 매핑하는 건 어때 ....

Array (n) .map ((value, index) ....)은 80 %입니다. 그러나 이상한 이유로 작동하지 않습니다. 그러나 해결 방법이 있습니다.

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

범위

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

홀수,이 두 반복자는 동일한 결과를 반환 : Array(end-start+1).entries()Array(end-start+1).fill().entries()

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.