faça <algo> N vezes (sintaxe declarativa)

90

Existe uma maneira em Javascript de escrever algo assim facilmente:

[1,2,3].times do {
  something();
}

Qualquer biblioteca que possa suportar alguma sintaxe semelhante, talvez?

Atualização: para esclarecer - gostaria something()de ser chamado 1,2 e 3 vezes, respectivamente, para cada iteração de elemento do array

BreakPhreak
fonte
1
Eu diria que não há nenhum recurso como esse no JS, e é um dos cinco principais recursos ausentes. É muito útil para testar software, mais do que qualquer coisa.
Alexander Mills

Respostas:

46

Esta resposta é baseada Array.forEach, sem qualquer biblioteca, apenas baunilha nativa .

Para ligar basicamente something()3 vezes, use:

[1,2,3].forEach(function(i) {
  something();
});

considerando a seguinte função:

function something(){ console.log('something') }

O outpout será

something
something
something

Para responder a essas perguntas, aqui está uma maneira de ligar something()1, 2 e 3 vezes, respectivamente:

É 2017, você pode usar o ES6:

[1,2,3].forEach(i => Array(i).fill(i).forEach(_ => {
  something()
}))

ou no bom e velho ES5:

[1,2,3].forEach(function(i) {
  Array(i).fill(i).forEach(function() {
    something()
  })
}))

Em ambos os casos, o outpout será

O outpout será

something

something
something

something
something
something

(uma, depois duas e três vezes)

vinil
fonte
18
Isso é incorreto porque não satisfaz esta parte da pergunta: 'Eu gostaria que algo () fosse chamado 1, 2 e 3 vezes'. Usando este código somethingé chamado apenas 3 vezes, ele deve ser chamado 6 vezes.
Ian Newson
Então eu acho que foi selecionada como a melhor resposta , pois pode ser um bom começo limpo.
vinyll de
3
Você também pode usar [...Array(i)]ou Array(i).fill(), dependendo de suas necessidades para os índices reais.
Guido Bouman de
Se você não estiver interessado em nenhum argumento passado, use.forEach(something)
kvsm
85

Basta usar um loop:

var times = 10;
for(var i=0; i < times; i++){
    doSomething();
}
ahren
fonte
3
obrigado! Eu gostaria de me beneficiar de uma sintaxe declarativa (assim como Jasmine etc)
BreakPhreak
certo, mas uma sintaxe declarativa de loop for funcional também seria muito melhor
Alexander Mills
69

Possível alternativa ES6.

Array.from(Array(3)).forEach((x, i) => {
  something();
});

E, se você quiser "ser chamado 1,2 e 3 vezes, respectivamente".

Array.from(Array(3)).forEach((x, i) => {
  Array.from(Array(i+1)).forEach((x, i2) => {
    console.log(`Something ${ i } ${ i2 }`)
  });
});

Atualizar:

Retirado de fill-arrays-with-undefined

Esta parece ser uma maneira mais otimizada de criar o array inicial, também atualizei para usar a segunda função de mapa de parâmetro sugerida por @felix-eve.

Array.from({ length: 3 }, (x, i) => {
  something();
});
Nverba
fonte
3
Devo ressaltar isso dizendo que está tudo bem se você estiver apenas criando um script rapidamente, mas o desempenho é horrível, então provavelmente não o use para recursão intensa ou em produção.
nverba de
Se você estiver indo para ES6, pode usar map () em vez de forEach ()
Andy Ford
3
Se concisão for a meta (e na verdade, mesmo que não seja), passe a função em vez de chamá-la:Array.from(Array(3)).forEach(something)
kvsm
1
Funciona com renderização de expressão de reação também.
Josh Sharkey
4
Array.from()tem um segundo parâmetro opcional mapFn, que permite executar uma função de mapa em cada elemento da matriz, portanto, não há necessidade de usar forEach. Você pode simplesmente fazer:Array.from({length: 3}, () => somthing() )
Felix Eve
19

Já que você mencionou o sublinhado:

Supondo que fseja a função que você deseja chamar:

_.each([1,2,3], function (n) { _.times(n, f) });

vai fazer o truque. Por exemplo, com f = function (x) { console.log(x); }, você obterá em seu console: 0 0 1 0 1 2

ggozad
fonte
Na verdade, pensei que você queria a separação.
ggozad de
2
_(3).times(function(n){return n;});deve fazer o truque. Veja a documentação aqui.
Chip
18

Com lodash :

_.each([1, 2, 3], (item) => {
   doSomeThing(item);
});

//Or:
_.each([1, 2, 3], doSomeThing);

Ou se você quiser fazer algo N vezes :

const N = 10;
_.times(N, () => {
   doSomeThing();
});

//Or shorter:
_.times(N, doSomeThing);

Consulte este link para lodashinstalação

Tho
fonte
12

Crie um Array e filltodos os itens com undefinedesse mapmétodo podem funcionar:

Array.fill não tem suporte para IE

Array(5).fill().map(()=>{ 
   // Do this 5 times:
   console.log(111) 
})


Usando o loop de reverências da velha guarda:

for( let i=5; i--; ){
   // Do this 5 times:
   console.log(222) 
}

vsync
fonte
Para ficar tranquilo, executei uma função uuid 50 mil vezes para garantir que nunca duplicasse um uuid. Então, travei o perfil do loop superior vs inferior apenas por diversão, apenas executando no meio de um carregamento de página normal usando ferramentas de desenvolvimento do cromo, se eu não estiver sendo burro, acho que é aproximadamente 1,2 bilhão em comparação com Array.indexOf () mais a geração de 50 mil uuids. newschool = 1º-5561,2ms 2º-5426,8ms | oldschool = 1st-4.966.3ms / 2nd-4.929.0ms Moral da história se você não estiver na faixa de um bilhão +, você nunca notaria uma diferença executando essas 200, 1k, até 10k vezes para fazer algo. Imaginei que alguém pudesse ser curioso como eu.
rifi2k de
Isso está correto e é conhecido há muitos anos. As diferentes abordagens não foram apresentadas para benefícios de velocidade, mas para suporte de navegadores mais antigos.
vsync de
2
Estranhamente, todos que lêem este tópico sabem que você não apresentou os exemplos para comparar a velocidade deles. Aconteceu de eu usá-los para fazer um pequeno teste e pensei em compartilhar algumas informações que alguém na estrada poderia achar interessante. Não estou muito certo porque não estava respondendo a uma pergunta, apenas exibindo informações e dando um lembrete para não se preocupar com a velocidade de um loop quando você está fazendo apenas algumas coisas que terminam em alguns ms de qualquer maneira. Também não é conhecido porque o mesmo teste de um ano atrás, ou seja, pode encontrar um 50% mais lento porque os navegadores mudam o tempo todo.
rifi2k de
9

Você também pode fazer a mesma coisa com a desestruturação da seguinte forma

[...Array(3)].forEach( _ => console.log('do something'));

ou se você precisa de índice

[...Array(3)].forEach(( _, index) => console.log('do something'));
Ozay Duman
fonte
7

Se você não pode usar Underscorejs, você mesmo pode implementá-lo. Ao anexar novos métodos aos protótipos Number e String, você poderia fazer assim (usando as funções de seta ES6):

// With String
"5".times( (i) => console.log("number "+i) );

// With number variable
var five = 5;
five.times( (i) => console.log("number "+i) );

// With number literal (parentheses required)
(5).times( (i) => console.log("number "+i) );

Você simplesmente precisa criar uma expressão de função (de qualquer nome) e atribuí-la a qualquer nome de propriedade (nos protótipos) que você gostaria de acessá-la como:

var timesFunction = function(callback) {
  if (typeof callback !== "function" ) {
    throw new TypeError("Callback is not a function");
  } else if( isNaN(parseInt(Number(this.valueOf()))) ) {
    throw new TypeError("Object is not a valid number");
  }
  for (var i = 0; i < Number(this.valueOf()); i++) {
    callback(i);
  }
};

String.prototype.times = timesFunction;
Number.prototype.times = timesFunction;
Andreas Bergström
fonte
1
Eu teria que reinvestigar o quão ruim é corrigir o protótipo, mas geralmente está tudo bem
Alexander Mills
2

Existe uma biblioteca fantástica chamada Ramda, que é semelhante a Underscore e Lodash, mas é mais poderosa.

const R = require('ramda');

R.call(R.times(() => {
    console.log('do something')
}), 5);

Ramda contém muitas funções úteis. Veja a documentação Ramda

Jan Bodnar
fonte
Adoro esta biblioteca como uma solução FP moderna e elegante.
momocow
1

Você pode usar o comprimento da matriz para executar o número de vezes que sua tarefa.

var arr = [1,2,3];

for(var i=0; i < arr.length; i++){
    doSomething();
}

ou

 var arr = [1,2,3];

 do
 {


 }
 while (i++ < arr.length);
Adil
fonte
1
times = function () {
    var length = arguments.length;
    for (var i = 0; i < length ; i++) {
        for (var j = 0; j < arguments[i]; j++) {
            dosomthing();
        }
    }
}

Você pode chamá-lo assim:

times(3,4);
times(1,2,3,4);
times(1,3,5,7,9);
XU3352
fonte
+1 - Utiliza a capacidade nativa do JavaScript de chamar funções com quantidades variáveis ​​de parâmetros. Nenhuma biblioteca extra necessária. Ótima solução
RustyTheBoyRobot
1
// calls doSomething 42 times
Array( 42 ).join( "x" ).split( "" ).forEach( doSomething );

e

// creates 42 somethings
var somethings = Array( 42 ).join( "x" ).split( "" ).map( () => buildSomething(); );

ou (via https://stackoverflow.com/a/20066663/275501 )

Array.apply(null, {length: 42}).forEach( doSomething );
goofballLogic
fonte
1
var times = [1,2,3];

for(var i = 0; i < times.length;  i++) {
  for(var j = 0; j < times[i];j++) {
     // do something
  }
}

Usando jQuery .each()

$([1,2,3]).each(function(i, val) {
  for(var j = 0; j < val;j++) {
     // do something
  }
});

OU

var x = [1,2,3];

$(x).each(function(i, val) {
  for(var j = 0; j < val;j++) {
     // do something
  }
});

EDITAR

Você pode fazer o seguinte com JS puro:

var times = [1,2,3];
times.forEach(function(i) {
   // do something
});
oecodeparadoxo
fonte
0

Basta usar um loop aninhado (talvez incluído em uma função)

function times( fct, times ) {
  for( var i=0; i<times.length; ++i ) {
    for( var j=0; j<times[i]; ++j ) {
      fct();
    }
  }
}

Então chame assim:

times( doSomething, [1,2,3] );
Sirko
fonte
0

Essas respostas estão todas boas e bem e IMO @Andreas é o melhor, mas muitas vezes em JS temos que fazer as coisas de forma assíncrona, nesse caso, assíncrono já cobre:

http://caolan.github.io/async/docs.html#times

const async = require('async');

async.times(5, function(n, next) {
    createUser(n, function(err, user) {
        next(err, user);
    });
}, function(err, users) {
    // we should now have 5 users
});

Esses recursos de 'tempos' não são muito úteis para a maioria dos códigos de aplicativos, mas devem ser úteis para testes.

Alexander Mills
fonte
0
const loop (fn, times) => {
  if (!times) { return }
  fn()
  loop(fn, times - 1)
}

loop(something, 3)
Goro
fonte
0

Dada uma função something:

function something() { console.log("did something") }

E um novo método timesadicionado ao Arrayprotótipo:

Array.prototype.times = function(f){
  for(v of this) 
    for(var _ of Array(v))
      f();
}

Este código:

[1,2,3].times(something)

Produz isto:

did something
did something
did something
did something
did something
did something

O que eu acho que responde à sua pergunta atualizada (5 anos depois), mas eu me pergunto o quão útil é ter este trabalho em um array? O efeito não seria o mesmo que chamar [6].times(something), que por sua vez poderia ser escrito como:

for(_ of Array(6)) something();

(embora o uso de _como uma variável lixo provavelmente irá prejudicar o lodash ou sublinhado se você estiver usando)

pix
fonte
1
É considerado uma má prática adicionar métodos personalizados a um objeto JS nativo.
Lior Elrom
Você pode usar letcomo em for (let _ of Array(6)) something()para evitar o derrame de lodash fora do por pelo menos.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
0

Array.from (ES6)

function doSomthing() {
    ...
}

Use-o assim:

Array.from(Array(length).keys()).forEach(doSomthing);

Ou

Array.from({ length }, (v, i) => i).forEach(doSomthing);

Ou

// array start counting from 1
Array.from({ length }, (v, i) => ++i).forEach(doSomthing);
Lior Elrom
fonte
0

Usando Array.frome .forEach.

let length = 5;
Array.from({length}).forEach((v, i) => {
  console.log(`#${i}`);
});

SeregPie
fonte
0

Supondo que possamos usar alguma sintaxe ES6 como o operador spread, vamos querer fazer algo tantas vezes quanto a soma de todos os números na coleção.

Neste caso, se times for igual a [1,2,3], o número total de vezes será 6, ou seja, 1 + 2 + 3.

/**
 * @param {number[]} times
 * @param {cb} function
 */
function doTimes(times, cb) {
  // Get the sum of all the times
  const totalTimes = times.reduce((acc, time) => acc + time);
  // Call the callback as many times as the sum
  [...Array(totalTimes)].map(cb);
}

doTimes([1,2,3], () => console.log('something'));
// => Prints 'something' 6 times

Esta postagem deve ser útil se a lógica por trás da construção e disseminação de um array não for aparente.

IliasT
fonte
0

Implementação de TypeScript:

Para aqueles que estão interessados ​​em como implementar String.timese Number.timesde uma forma que seja segura para o tipo e funcione com o thisArg, aqui está:

declare global {
    interface Number {
        times: (callbackFn: (iteration: number) => void, thisArg?: any) => void;
    }
    interface String {
        times: (callbackFn: (iteration: number) => void, thisArg?: any) => void;
    }
}

Number.prototype.times = function (callbackFn, thisArg) {
    const num = this.valueOf()
    if (typeof callbackFn !== "function" ) {
        throw new TypeError("callbackFn is not a function")
    }
    if (num < 0) {
        throw new RangeError('Must not be negative')
    }
    if (!isFinite(num)) {
        throw new RangeError('Must be Finite')
    }
    if (isNaN(num)) {
        throw new RangeError('Must not be NaN')
    }

    [...Array(num)].forEach((_, i) => callbackFn.bind(thisArg, i + 1)())
    // Other elegant solutions
    // new Array<null>(num).fill(null).forEach(() => {})
    // Array.from({length: num}).forEach(() => {})
}

String.prototype.times = function (callbackFn, thisArg) {
    let num = parseInt(this.valueOf())
    if (typeof callbackFn !== "function" ) {
        throw new TypeError("callbackFn is not a function")
    }
    if (num < 0) {
        throw new RangeError('Must not be negative')
    }
    if (!isFinite(num)) {
        throw new RangeError('Must be Finite')
    }
    // num is NaN if `this` is an empty string 
    if (isNaN(num)) {
        num = 0
    }

    [...Array(num)].forEach((_, i) => callbackFn.bind(thisArg, i + 1)())
    // Other elegant solutions
    // new Array<null>(num).fill(null).forEach(() => {})
    // Array.from({length: num}).forEach(() => {})
}

Um link para o Playground TypeScript com alguns exemplos pode ser encontrado aqui

Esta postagem implementa soluções postadas por: Andreas Bergström , vinyll , Ozay Duman , & SeregPie

Noah Anderson
fonte