O JavaScript possui um método como "range ()" para gerar um intervalo dentro dos limites fornecidos?


873

Em PHP, você pode fazer ...

range(1, 3); // Array(1, 2, 3)
range("A", "C"); // Array("A", "B", "C")

Ou seja, existe uma função que permite obter um intervalo de números ou caracteres passando os limites superior e inferior.

Existe algo embutido no JavaScript nativamente para isso? Caso contrário, como eu o implementaria?


1
Prototype.js tem a $Rfunção, mas, além disso, acho que não.
Yi Jiang

Este (relacionados) pergunta tem alguns excelentes respostas: stackoverflow.com/questions/6299500/...
btk

Array.from("ABC") //['A', 'B', 'C']Essa é a coisa mais próxima que posso encontrar para a segunda parte da pergunta.
21816 Andrew_1510

@ Andrew_1510 Você pode usar split("")também
alex

1
Quando é amante obrigado zerar esta oneliner:Array.apply(null, { length: 10 }).map(eval.call, Number)
csharpfolk

Respostas:


1502

Números

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

Iteração de caracteres

String.fromCharCode(...[...Array('D'.charCodeAt(0) - 'A'.charCodeAt(0) + 1).keys()].map(i => i + 'A'.charCodeAt(0)));
 => "ABCD"

Iteração

for (const x of Array(5).keys()) {
  console.log(x, String.fromCharCode('A'.charCodeAt(0) + x));
}
 => 0,"A" 1,"B" 2,"C" 3,"D" 4,"E"

Como funções

function range(size, startAt = 0) {
    return [...Array(size).keys()].map(i => i + startAt);
}

function characterRange(startChar, endChar) {
    return String.fromCharCode(...range(endChar.charCodeAt(0) -
            startChar.charCodeAt(0), startChar.charCodeAt(0)))
}

Como funções digitadas

function range(size:number, startAt:number = 0):ReadonlyArray<number> {
    return [...Array(size).keys()].map(i => i + startAt);
}

function characterRange(startChar:string, endChar:string):ReadonlyArray<string> {
    return String.fromCharCode(...range(endChar.charCodeAt(0) -
            startChar.charCodeAt(0), startChar.charCodeAt(0)))
}

_.range()função lodash.js

_.range(10);
 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
 => [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
 => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
String.fromCharCode(..._.range('A'.charCodeAt(0), 'D'.charCodeAt(0) + 1));
 => "ABCD"

Navegadores antigos não es6 sem uma biblioteca:

Array.apply(null, Array(5)).map(function (_, i) {return i;});
 => [0, 1, 2, 3, 4]

console.log([...Array(5).keys()]);

(Crédito ES6 a nils petersohn e outros comentadores)


72
Porque se é útil em qualquer lugar, provavelmente é útil em JS. (JS pode fazer funcional coisas tipo de programação, o que pode beneficiar de uma gama (0 afirmação de que e mil outras razões, pode ser útil em alguns casos semirare).
Lodewijk

5
Alguma idéia de por que simplesmente usar (new Array(5)).map(function (value, index) { return index; })não funcionaria? Isso retorna [undefined × 5]para mim no DevTools do Chrome.
Lewis

12
@ Lewis Porque um array definido com slots vazios que não serão repetidos com map()ou com um de seus amigos.
22415 alex

65
Array.from (Array (5) .keys ())
nils petersohn

17
Array(5).fill()também é
mapeável

333

Para números, você pode usar o ES6 Array.from(), que funciona em tudo hoje em dia, exceto no IE:

Versão mais curta:

Array.from({length: 20}, (x,i) => i);

Versão mais longa:

Array.from(new Array(20), (x,i) => i)

que cria uma matriz de 0 a 19 inclusive. Isso pode ser reduzido ainda mais para uma destas formas:

Array.from(Array(20).keys())
// or
[...Array(20).keys()]

Os limites inferior e superior também podem ser especificados, por exemplo:

Array.from(new Array(20), (x,i) => i + *lowerBound*)

Um artigo que descreve isso com mais detalhes: http://www.2ality.com/2014/05/es6-array-methods.html


50
O primeiro exemplo pode até ser simplificado para [... Matriz (20) .keys ()]
Delapouite

27
Um pouco mais sucinto que o Array.from()método e mais rápido que ambos:Array(20).fill().map((_, i) => i)
Stu Cox

2
@Delapouite Awesome! Você deve fazer disso uma resposta separada, e eu votarei nela! É também a resposta perfeita para essa duplicata .
Jib

9
@Delapouite @jib E isso também:Array.from({length: end - start}, (v, k) => k + start)
Aditya Singh

1
@ icc97 Sim, os linters podem reclamar, embora no JavaScript omitir um argumento de função definido como o mesmo que passar undefined, portanto fill()(sem argumento) não está errado por si só. O valor de preenchimento não é usado nessa solução; portanto, se você quiser, pode fill(0)salvar alguns caracteres.
Stu Cox

122

Meu novo formulário favorito ( ES2015 )

Array(10).fill(1).map((x, y) => x + y)

E se você precisar de uma função com um stepparâmetro:

const range = (start, stop, step = 1) =>
  Array(Math.ceil((stop - start) / step)).fill(start).map((x, y) => x + y * step)

5
deixar gama = (arranque, paragem, passo = 1) => Array (paragem - início) .Reabastecer (iniciar) .Map ((x, y) => x + y * passo)
rodfersou

4
@rodfersou FYI: seu exemplo está errado. stopnão é realmente a posição de parada / fim, mas conta / distância. (sem ofensas, apenas para conscientizar as pessoas sobre o erro de digitação) #
F Lekschas

4
Para os confusos - devido à edição de rodfersou após o comentário de F Lekschas, seu código está agora correto.
eedrah

1
O argumento para o qual você passa Array(Math.ceil((stop - start) / step) + 1), precisa +1no final, realmente imitar o comportamento "inclusivo" do php.
Johan Dettmar 26/10/19

3
Esta é a resposta principal que realmente responde à pergunta completa de uma função Javascript que implementa completamente um rangemétodo. Todos os outros atualmente acima deste (exceto para lodash de _.range) implementar iteradores básicas em vez de uma função de alcance real com iniciar, parar e passo
icc97

99

Aqui estão os meus 2 centavos:

function range(start, count) {
  return Array.apply(0, Array(count))
    .map((element, index) => index + start);
}

1
Excelente uso de funções de alta ordem.
Farzad YZ

5
Na verdade, isso está errado, porque a pergunta está pedindo valores de início e fim. Não iniciar e contar / distância.
James Robey 31/03

73

Funciona para caracteres e números, avançando ou retrocedendo com uma etapa opcional.

var range = function(start, end, step) {
    var range = [];
    var typeofStart = typeof start;
    var typeofEnd = typeof end;

    if (step === 0) {
        throw TypeError("Step cannot be zero.");
    }

    if (typeofStart == "undefined" || typeofEnd == "undefined") {
        throw TypeError("Must pass start and end arguments.");
    } else if (typeofStart != typeofEnd) {
        throw TypeError("Start and end arguments must be of same type.");
    }

    typeof step == "undefined" && (step = 1);

    if (end < start) {
        step = -step;
    }

    if (typeofStart == "number") {

        while (step > 0 ? end >= start : end <= start) {
            range.push(start);
            start += step;
        }

    } else if (typeofStart == "string") {

        if (start.length != 1 || end.length != 1) {
            throw TypeError("Only strings with one character are supported.");
        }

        start = start.charCodeAt(0);
        end = end.charCodeAt(0);

        while (step > 0 ? end >= start : end <= start) {
            range.push(String.fromCharCode(start));
            start += step;
        }

    } else {
        throw TypeError("Only string and number types are supported");
    }

    return range;

}

jsFiddle .

Se você gosta de aumentar tipos nativos, atribua-o a Array.range.


53

Função de faixa simples:

function range(start, stop, step) {
    var a = [start], b = start;
    while (b < stop) {
        a.push(b += step || 1);
    }
    return a;
}

Para incorporar o tipo de dados BitInt, algumas verificações podem ser incluídas, garantindo que todas as variáveis ​​sejam iguais typeof start:

function range(start, stop, step) {
    var a = [start], b = start;
    if (typeof start == 'bigint') {
        stop = BigInt(stop)
        step = step? BigInt(step): 1n;
    } else
        step = step || 1;
    while (b < stop) {
        a.push(b += step);
    }
    return a;
}

Remover valores mais altos do que os definidos por stopexemplo range(0,5,2)incluirá 6, o que não deveria ser.

function range(start, stop, step) {
    var a = [start], b = start;
    while (b < stop) {
        a.push(b += step || 1);
    }
    return (b > stop) ? a.slice(0,-1) : a;
}

3
MAIS UNO para utilizável e legível. Melhor trecho de código que já vi há muito tempo.
precisa saber é

1
Isso não funciona quando step != 1, a whilecondição precisa levar stepem consideração. Minha versão atualizada com um stepvalor padrão : range de funções (start, stop, step) {step = step || 1 var a = [início], b = início; while ((b + etapa) <parada) {console.log ("b:" + b + ". a:" + a + "."); b + = passo; a.push (b); } retornar a; }
daveharris 25/10

Adicionei uma etapa padrão acima (step || 1),.
Sr. Polywhirl

36
Array.range= function(a, b, step){
    var A= [];
    if(typeof a== 'number'){
        A[0]= a;
        step= step || 1;
        while(a+step<= b){
            A[A.length]= a+= step;
        }
    }
    else{
        var s= 'abcdefghijklmnopqrstuvwxyz';
        if(a=== a.toUpperCase()){
            b=b.toUpperCase();
            s= s.toUpperCase();
        }
        s= s.substring(s.indexOf(a), s.indexOf(b)+ 1);
        A= s.split('');        
    }
    return A;
}


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

    Array.range(-100,100,20);
    // [-100,-80,-60,-40,-20,0,20,40,60,80,100]

    Array.range('A','F');
    // ['A','B','C','D','E','F')

    Array.range('m','r');
    // ['m','n','o','p','q','r']

Você realmente não deve usar métodos de manipulação no Arrayprotótipo.
connectyourcharger

Este método funciona apenas com números inteiros e caracteres. Se os parâmetros forem nulos, indefinidos, NaN, booleanos, matriz, objeto etc., esse método retornará o seguinte erro undefined method toUpperCase to etc:!
Victor

`` if (typeof from! == 'number' && typeof from! == 'string') {throw new TypeError ('O primeiro parâmetro deve ser um número ou um caractere')} if (typeof to! == ' number '&& typeof to! ==' string ') {throw new TypeError (' O primeiro parâmetro deve ser um número ou um caractere ')} `` `
Victor

36

OK, em JavaScript, não temos uma range()função como PHP , por isso precisamos criar a função que é bastante fácil, escrevo algumas funções de uma linha para você e as separo para Numbers e Alphabets, como abaixo:

para números :

function numberRange (start, end) {
  return new Array(end - start).fill().map((d, i) => i + start);
}

e chame assim:

numberRange(5, 10); //[5, 6, 7, 8, 9]

para alfabetos :

function alphabetRange (start, end) {
  return new Array(end.charCodeAt(0) - start.charCodeAt(0)).fill().map((d, i) => String.fromCharCode(i + start.charCodeAt(0)));
}

e chame assim:

alphabetRange('c', 'h'); //["c", "d", "e", "f", "g"]

2
Eu acho que existem erros off-by-one nessas funções. Deveria ser Array(end - start + 1)e Array(end.charCodeAt(0) - start.charCodeAt(0) + 1).
earcanal

24

Função útil para executar o truque, execute o trecho de código abaixo

function range(start, end, step, offset) {
  
  var len = (Math.abs(end - start) + ((offset || 0) * 2)) / (step || 1) + 1;
  var direction = start < end ? 1 : -1;
  var startingPoint = start - (direction * (offset || 0));
  var stepSize = direction * (step || 1);
  
  return Array(len).fill(0).map(function(_, index) {
    return startingPoint + (stepSize * index);
  });
  
}

console.log('range(1, 5)=> ' + range(1, 5));
console.log('range(5, 1)=> ' + range(5, 1));
console.log('range(5, 5)=> ' + range(5, 5));
console.log('range(-5, 5)=> ' + range(-5, 5));
console.log('range(-10, 5, 5)=> ' + range(-10, 5, 5));
console.log('range(1, 5, 1, 2)=> ' + range(1, 5, 1, 2));

aqui está como usá-lo

range (Início, Fim, Passo = 1, Deslocamento = 0);

  • inclusivo - encaminhar range(5,10) // [5, 6, 7, 8, 9, 10]
  • inclusivo - para trás range(10,5) // [10, 9, 8, 7, 6, 5]
  • Um passo atrás range(10,2,2) // [10, 8, 6, 4, 2]
  • exclusivo - encaminhar range(5,10,0,-1) // [6, 7, 8, 9] not 5,10 themselves
  • deslocamento - expandir range(5,10,0,1) // [4, 5, 6, 7, 8, 9, 10, 11]
  • offset - encolher range(5,10,0,-2) // [7, 8]
  • passo - expandir range(10,0,2,2) // [12, 10, 8, 6, 4, 2, 0, -2]

espero que você ache útil.


E aqui está como isso funciona.

Basicamente, estou primeiro calculando o comprimento da matriz resultante e criando uma matriz preenchida com zero nesse comprimento, depois preenchendo-a com os valores necessários

  • (step || 1)=> E outros como este meio usar o valor do stepe se não foi fornecido o uso 1em vez
  • Começamos calculando o comprimento da matriz de resultados usando-a (Math.abs(end - start) + ((offset || 0) * 2)) / (step || 1) + 1)para simplificar (diferença * deslocamento na direção / etapa)
  • Depois de obter o comprimento, criamos uma matriz vazia com valores inicializados usando new Array(length).fill(0); check aqui
  • Agora temos uma matriz [0,0,0,..]com o comprimento que queremos. Mapeamos sobre ele e retornamos uma nova matriz com os valores que precisamos usandoArray.map(function() {})
  • var direction = start < end ? 1 : 0;Obviamente, se startnão for menor do que o endnecessário, precisamos retroceder. Quero dizer, passando de 0 a 5 ou vice-versa
  • Em cada iteração, startingPoint+ stepSize* indexwill nos fornece o valor que precisamos

8
Útil, certamente. Simples? Eu peço desculpa mas não concordo; independentemente de você torná-lo um forro. Vindo de Python, isso é um choque.
precisa saber é o seguinte

@ PascalvKooten, sim, claro que teria sido ótimo se houvesse um método embutido para esse tipo de python, eu acho, mas esse foi o mais simples que eu poderia encontrar. E provou ser útil em meus projetos.
Azerafati 03/03

Postando um trecho de código dolorosamente complexo como esse, especialmente como uma linha e sem explicação de como ele funciona? Mau exemplo de uma boa resposta SO, independentemente de ela "funcionar" ou não.
precisa saber é o seguinte

1
@ Madbreaks, sim, você está certo. Eu tenho sido ingênuo para torná-lo único. só queria dar a todos uma solução rápida e fácil
azerafati

22
var range = (l,r) => new Array(r - l).fill().map((_,k) => k + l);

@nikkwong, _é apenas um nome de argumento no retorno de chamada de mapeamento. Você sabe, em alguns idiomas, você usaria o _como um nome para indicar que a variável não é usada.
Klesun #

Aqui, porém, _não é passado os argumentos para range. Por que não?
Nikk wong

2
Muito arrumado! Embora, é importante notar que não funciona em nenhum IE ou Opera.
Rafael Xavier

4
Essa resposta precisa de explicação, pois ela é uma má opção para SO.
precisa saber é o seguinte

@RafaelXavier irá trabalhar no IE com o Array.fill () polyfill
mwag

18

Usando o operador de propagação Harmony e as funções de seta:

var range = (start, end) => [...Array(end - start + 1)].map((_, i) => start + i);

Exemplo:

range(10, 15);
[ 10, 11, 12, 13, 14, 15 ]

essa é a melhor resposta!
Henry H.

1
Não é o mais rápido.
Mjwrazor

O que o símbolo de sublinhado '_' representa neste caso?
Oleh Berehovskyi 27/02/19

@OlehBerehovskyi Significa um parâmetro de função lambda que você não tem a intenção de realmente usar. Um ponteiro que avisa sobre variáveis ​​não utilizadas deve ignorá-lo.
Micah Zoltu 16/03/19

18

--- ATUALIZAÇÃO (Agradecimentos a @lokhmakov pela simplificação) ---

Outra versão usando geradores ES6 (veja a excelente resposta de Paolo Moretti com geradores ES6 ):

const RANGE = (x,y) => Array.from((function*(){
  while (x <= y) yield x++;
})());

console.log(RANGE(3,7));  // [ 3, 4, 5, 6, 7 ]

Ou, se precisarmos apenas de iterável, então:

const RANGE_ITER = (x,y) => (function*(){
  while (x <= y) yield x++;
})();

for (let n of RANGE_ITER(3,7)){
  console.log(n);
}

// 3
// 4
// 5
// 6
// 7

--- O código original era: ---

const RANGE = (a,b) => Array.from((function*(x,y){
  while (x <= y) yield x++;
})(a,b));

e

const RANGE_ITER = (a,b) => (function*(x,y){
  while (x <= y) yield x++;
})(a,b);

1
Just const range = (x, y) => Array.from(function* () { while (x <= y) yield x++; }())
lokhmakov 30/03

@lokhmakov Sim, você está certo. obrigado! Basta aplicar seu código na minha resposta.
Hero Qu

15

Fiz algumas pesquisas sobre várias funções de intervalo. Confira a comparação do jsperf das diferentes maneiras de executar essas funções. Certamente não é uma lista perfeita ou exaustiva, mas deve ajudar :)

O vencedor é...

function range(lowEnd,highEnd){
    var arr = [],
    c = highEnd - lowEnd + 1;
    while ( c-- ) {
        arr[c] = highEnd--
    }
    return arr;
}
range(0,31);

Tecnicamente, não é o mais rápido no Firefox, mas a louca diferença de velocidade (imho) no Chrome compensa isso.

Também é interessante observar o quão rápido o cromo é com essas funções de matriz do que o firefox. O Chrome é pelo menos 4 ou 5 vezes mais rápido .


Note-se que esta foi comparada com funções de gama, que incluiu um parâmetro de tamanho do passo
binaryfunt

15

O Javascript padrão não possui uma função interna para gerar intervalos. Várias estruturas javascript adicionam suporte para esses recursos ou, como outros já apontaram, você sempre pode criar seus próprios.

Se você deseja verificar novamente, o recurso definitivo é o padrão ECMA-262 .


Embora eu tenha certeza de uma resposta perfeitamente boa em 2010, essa não deve mais ser considerada a melhor abordagem. Você não deve se estender construído em tipos, como Prototype.js tendia a fazer 👍
Dana Woodman

@DanaWoodman obrigado por trazer isso - Eu atualizei a resposta para tirar a referência a Prototype.js uma vez que é na verdade bastante obsoletos em 2018
Mike Dinescu

21
Bem, isso não ajudou em nada.
Pithikos 01/04/19

@Pithikos Vejo que essa pergunta foi editada desde que foi originalmente solicitada e o OP queria saber se existe uma função de faixa nativa no JS.
Mike Dinescu

13

Você pode usar lodash ou Undescore.js range :

var range = require('lodash/range')
range(10)
// -> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

Como alternativa, se você precisar apenas de um intervalo consecutivo de números inteiros, poderá fazer algo como:

Array.apply(undefined, { length: 10 }).map(Number.call, Number)
// -> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

No ES6 rangepode ser implementado com geradores :

function* range(start=0, end=null, step=1) {
  if (end == null) {
    end = start;
    start = 0;
  }

  for (let i=start; i < end; i+=step) {
    yield i;
  }
}

Essa implementação economiza memória ao iterar grandes seqüências, porque não precisa materializar todos os valores em uma matriz:

for (let i of range(1, oneZillion)) {
  console.log(i);
}

A parte ES6 agora é a resposta correta para esta pergunta. Eu recomendaria remover as outras partes, que são cobertas por outras respostas.
Johs # 8/15

os geradores são um tanto estranhos se usados ​​fora de um loop: x = range (1, 10); // {} x; // {} // parece um mapa vazio WTF!?! . x.next () valor; // OK 1; x [3] // indefinido, apenas com matriz verdadeiro
Anona112

@ Anona112 você pode usar Array.frompara converter geradores em instâncias de array e inspecionar a saída.
Paolo Moretti

10

Um desafio interessante seria escrever a função mais curta para fazer isso. Recursão ao resgate!

function r(a,b){return a>b?[]:[a].concat(r(++a,b))}

Tende a ser lento em grandes faixas, mas felizmente os computadores quânticos estão ao virar da esquina.

Um bônus adicional é que é ofuscante. Porque todos sabemos o quanto é importante ocultar nosso código de olhares indiscretos.

Para ofuscar verdadeira e totalmente a função, faça o seguinte:

function r(a,b){return (a<b?[a,b].concat(r(++a,--b)):a>b?[]:[a]).sort(function(a,b){return a-b})}

4
Curto! = Simples, mas mais simples é melhor. Aqui está uma versão mais fácil de ler const range = (a, b) => (a>=b) ? [] : [a, ...range(a+1, b)]
:,

1
@nafg: const range = (a, b, Δ = 1) => (a > b) ? [] : [a, ...range(a + Δ, b, Δ)];. Também votando a resposta inteira para o comentário.
7vujy0f0hy

10

Esta pode não ser a melhor maneira. Mas se você deseja obter um intervalo de números em uma única linha de código. Por exemplo 10 - 50

Array(40).fill(undefined).map((n, i) => i + 10)

Onde 40 é (fim - começo) e 10 é o começo. Isso deve retornar [10, 11, ..., 50]


9

Eu codificaria algo como isto:

function range(start, end) {
    return Array(end-start).join(0).split(0).map(function(val, id) {return id+start});
}  

range(-4,2);
// [-4,-3,-2,-1,0,1]

range(3,9);
// [3,4,5,6,7,8]

Ele se comporta de maneira semelhante ao intervalo do Python:

>>> range(-4,2)
[-4, -3, -2, -1, 0, 1]

8

Uma implementação bastante minimalista que emprega fortemente o ES6 pode ser criada da seguinte maneira, chamando atenção especial para o Array.from()método estático:

const getRange = (start, stop) => Array.from(
  new Array((stop - start) + 1),
  (_, i) => i + start
);

Como uma observação lateral, criei um Gist no qual criei uma getRange()espécie de função "aprimorada" . Em particular, pretendi capturar casos extremos que podem não ser tratados na variante básica acima. Além disso, adicionei suporte para intervalos alfanuméricos. Em outras palavras, chamá-lo com duas entradas fornecidas, como 'C'e 'K'(nessa ordem), retorna uma matriz cujos valores são o conjunto seqüencial de caracteres da letra 'C' (inclusive) através da letra 'K' (exclusiva):getRange('C', 'K'); // => ["C", "D", "E", "F", "G", "H", "I", "J"]
IsenrichO

você não precisa da newpalavra
Soldeplata Saketos 14/03/19

8

range(start,end,step): Com iteradores ES6

Você só pede limites superior e inferior. Aqui nós criamos um com um passo também.

Você pode criar facilmente a range()função de gerador, que pode funcionar como um iterador. Isso significa que você não precisa pré-gerar toda a matriz.

function * range ( start, end, step = 1 ) {
  let state = start;
  while ( state < end ) {
    yield state;
    state += step;
  }
  return;
};

Agora você pode criar algo que pré-gera a matriz do iterador e retorna uma lista. Isso é útil para funções que aceitam uma matriz. Para isso, podemos usarArray.from()

const generate_array = (start,end,step) =>
  Array.from( range(start,end,step) );

Agora você pode gerar uma matriz estática facilmente,

const array1 = generate_array(1,10,2);
const array1 = generate_array(1,7);

Mas quando algo deseja um iterador (ou oferece a opção de usar um iterador), você também pode facilmente criar um.

for ( const i of range(1, Number.MAX_SAFE_INTEGER, 7) ) {
  console.log(i)
}

Notas especiais


7

Embora isso não seja do PHP , mas uma imitação rangedo Python .

function range(start, end) {
    var total = [];

    if (!end) {
        end = start;
        start = 0;
    }

    for (var i = start; i < end; i += 1) {
        total.push(i);
    }

    return total;
}

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

+1 para o mais rápido. com uma matriz de -36768 - 36768, levou 3ms, o segundo lugar foi de 13 ms e possui linhas vermelhas IDE.
Mjwrazor #

7

Na medida em que gera uma matriz numérica para um determinado intervalo, eu uso o seguinte:

function range(start, stop)
{
    var array = [];

    var length = stop - start; 

    for (var i = 0; i <= length; i++) { 
        array[i] = start;
        start++;
    }

    return array;
}

console.log(range(1, 7));  // [1,2,3,4,5,6,7]
console.log(range(5, 10)); // [5,6,7,8,9,10]
console.log(range(-2, 3)); // [-2,-1,0,1,2,3]

Obviamente, não funcionará para matrizes alfabéticas.


A configuração array = []dentro do loop pode não dar o que você deseja.
2328 alex

@ alex, obrigado. Você está certo, eu também esqueci de incrementar o parâmetro "start" em cada passagem do loop. Está consertado agora.
Jhaskell # 24/15

Ele ainda não produzirá a saída desejada; se eu quiser um intervalo de 5 a 10, isso me dará [5, 6, 7, 8, 9, 10, 11, 12, 13, 14], eu esperaria apenas a primeira metade desse array.
24415 alex

@ alex, obrigado novamente, eu não tinha considerado uma restrição de comprimento com base na entrada. Veja a versão atualizada.
Jhaskell # 25/15

6

Usando geradores Harmony , suportados por todos os navegadores, exceto o IE11 :

var take = function (amount, generator) {
    var a = [];

    try {
        while (amount) {
            a.push(generator.next());
            amount -= 1;
        }
    } catch (e) {}

    return a;
};

var takeAll = function (gen) {
    var a = [],
        x;

    try {
        do {
            x = a.push(gen.next());
        } while (x);
    } catch (e) {}

    return a;
};

var range = (function (d) {
    var unlimited = (typeof d.to === "undefined");

    if (typeof d.from === "undefined") {
        d.from = 0;
    }

    if (typeof d.step === "undefined") {
        if (unlimited) {
            d.step = 1;
        }
    } else {
        if (typeof d.from !== "string") {
            if (d.from < d.to) {
                d.step = 1;
            } else {
                d.step = -1;
            }
        } else {
            if (d.from.charCodeAt(0) < d.to.charCodeAt(0)) {
                d.step = 1;
            } else {
                d.step = -1;
            }
        }
    }

    if (typeof d.from === "string") {
        for (let i = d.from.charCodeAt(0); (d.step > 0) ? (unlimited ? true : i <= d.to.charCodeAt(0)) : (i >= d.to.charCodeAt(0)); i += d.step) {
            yield String.fromCharCode(i);
        }
    } else {
        for (let i = d.from; (d.step > 0) ? (unlimited ? true : i <= d.to) : (i >= d.to); i += d.step) {
            yield i;
        }
    }
});

Exemplos

toma

Exemplo 1.

take leva apenas o máximo possível

take(10, range( {from: 100, step: 5, to: 120} ) )

retorna

[100, 105, 110, 115, 120]

Exemplo 2

to não necessário

take(10, range( {from: 100, step: 5} ) )

retorna

[100, 105, 110, 115, 120, 125, 130, 135, 140, 145]

leve tudo

Exemplo 3

from não necessário

takeAll( range( {to: 5} ) )

retorna

[0, 1, 2, 3, 4, 5]

Exemplo 4

takeAll( range( {to: 500, step: 100} ) )

retorna

[0, 100, 200, 300, 400, 500]

Exemplo 5

takeAll( range( {from: 'z', to: 'a'} ) )

retorna

["z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n", "m", "l", "k", "j", "i", "h", "g", "f", "e", "d", "c", "b", "a"]


Editado com minhas sugestões :)
Xotic750 12/06

+1 para a abordagem. Para o argumento de @ alex, não ter operações ternárias (especialmente não aninhadas) na forcláusula melhoraria a legibilidade aqui.
Justin Johnson

5

... mais alcance, usando uma função de gerador.

function range(s, e, str){
  // create generator that handles numbers & strings.
  function *gen(s, e, str){
    while(s <= e){
      yield (!str) ? s : str[s]
      s++
    }
  }
  if (typeof s === 'string' && !str)
    str = 'abcdefghijklmnopqrstuvwxyz'
  const from = (!str) ? s : str.indexOf(s)
  const to = (!str) ? e : str.indexOf(e)
  // use the generator and return.
  return [...gen(from, to, str)]
}

// usage ...
console.log(range('l', 'w'))
//=> [ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' ]

console.log(range(7, 12))
//=> [ 7, 8, 9, 10, 11, 12 ]

// first 'o' to first 't' of passed in string.
console.log(range('o', 't', "ssshhhooooouuut!!!!"))
// => [ 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 't' ]

// only lowercase args allowed here, but ...
console.log(range('m', 'v').map(v=>v.toUpperCase()))
//=> [ 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V' ]

// => and decreasing range ...
console.log(range('m', 'v').map(v=>v.toUpperCase()).reverse())

// => ... and with a step
console.log(range('m', 'v')
          .map(v=>v.toUpperCase())
          .reverse()
          .reduce((acc, c, i) => (i % 2) ? acc.concat(c) : acc, []))

// ... etc, etc.

Espero que isso seja útil.


5

Meu colega de trabalho de codegolfe veio com isso (ES6), inclusive:

(s,f)=>[...Array(f-s+1)].map((e,i)=>i+s)

não inclusivo:

(s,f)=>[...Array(f-s)].map((e,i)=>i+s)


4

O d3 também possui uma função de alcance embutida. Consulte https://github.com/mbostock/d3/wiki/Arrays#d3_range :

d3.range ([start,] stop [, step])

Gera uma matriz que contém uma progressão aritmética, semelhante ao intervalo interno do Python. Esse método é frequentemente usado para iterar sobre uma sequência de valores numéricos ou inteiros, como os índices em uma matriz. Diferentemente da versão do Python, os argumentos não precisam ser números inteiros, embora os resultados sejam mais previsíveis se forem devido à precisão do ponto flutuante. Se a etapa for omitida, o padrão será 1.

Exemplo:

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

Eu nunca soube que D3 existia. Não usará o método range, mas usará este pacote.
Mjwrazor 9/05

Muito obrigado. Eu uso o D3 e estava procurando um método JS nativo, sem saber que eu o D3 já o oferece.
Cezar

4

Implementação completa do ES6 usando a assinatura de intervalo ([start,] stop [, step]):

function range(start, stop, step=1){
  if(!stop){stop=start;start=0;}
  return Array.from(new Array(int((stop-start)/step)), (x,i) => start+ i*step)
}

Se você quiser um passo negativo automático, adicione

if(stop<start)step=-Math.abs(step)

Ou mais minimalisticamente:

range=(b, e, step=1)=>{
  if(!e){e=b;b=0}
  return Array.from(new Array(int((e-b)/step)), (_,i) => b<e? b+i*step : b-i*step)
}

Se você tem grandes faixas, observe a abordagem de gerador de Paolo Moretti


Substitua !stoppor typeof stop === 'undefined', substitua intpor Math.floore adicione uma verificação if (start > stop && step > 0)(caso contrário, range(-3, -10)lança uma exceção em vez de fazer algo sensato (inverter o sinal da etapa ou retornar [])). Caso contrário, bom!
Ahmed Fasih

4

Existe um módulo npm bereich para isso ("bereich" é a palavra alemã para "range"). Utiliza os iteradores modernos do JavaScript, para que você possa usá-lo de várias maneiras, como:

console.log(...bereich(1, 10));
// => 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

const numbers = Array.from(bereich(1, 10));
// => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

for (const number of bereich(1, 10)) {
  // ...
}

Ele também suporta intervalos descendentes (simplesmente trocando mine max) e também suporta outras etapas que não 1.

Isenção de responsabilidade: eu sou o autor deste módulo; portanto, responda com um grão de sal.


4

Este também funciona ao contrário.

const range = ( a , b ) => Array.from( new Array( b > a ? b - a : a - b ), ( x, i ) => b > a ? i + a : a - i );

range( -3, 2 ); // [ -3, -2, -1, 0, 1 ]
range( 1, -4 ); // [ 1, 0, -1, -2, -3 ]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.