hacer <algo> N veces (sintaxis declarativa)

97

¿Hay alguna forma en Javascript de escribir algo como esto fácilmente?

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

¿Alguna biblioteca que pueda soportar alguna sintaxis similar tal vez?

Actualización: para aclarar, me gustaría que me something()llamen 1, 2 y 3 veces respectivamente para cada iteración del elemento de la matriz

BreakPhreak
fuente
2
Yo diría que no hay una característica como esta en JS, y es una de las cinco características que faltan. Es muy útil para probar software, más que nada.
Alexander Mills

Respuestas:

48

Esta respuesta se basa Array.forEach, sin ninguna biblioteca, solo vainilla nativa .

Básicamente, para llamar something()3 veces, use:

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

considerando la siguiente función:

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

La salida será

something
something
something

Para completar estas preguntas, aquí tienes una forma de llamar something()1, 2 y 3 veces respectivamente:

Es 2017, puede usar ES6:

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

o en el viejo ES5:

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

En ambos casos, la salida será

La salida será

something

something
something

something
something
something

(una vez, luego dos, luego 3 veces)

Vinil
fuente
18
Esto es incorrecto porque no satisface esta parte de la pregunta: 'Me gustaría que algo () se llamara 1,2 y 3 veces'. El uso de este código somethingsolo se llama 3 veces, debe llamarse 6 veces.
Ian Newson
Entonces supongo que ha sido seleccionada como la mejor respuesta, ya que puede ser un buen comienzo más limpio.
Vinyll
3
También puede usar [...Array(i)]o Array(i).fill(), según sus necesidades para los índices reales.
Guido Bouman
Si no está interesado en los argumentos aprobados, use.forEach(something)
kvsm
88

Solo usa un bucle:

var times = 10;
for(var i=0; i < times; i++){
    doSomething();
}
ahren
fuente
3
¡gracias! Me gustaría beneficiarme de una sintaxis declarativa (como Jasmine, etc.)
BreakPhreak
correcto, pero una sintaxis declarativa funcional para bucle también sería mucho mejor
Alexander Mills
73

Posible alternativa a ES6.

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

Y, si quieres que "se llame 1,2 y 3 veces respectivamente".

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

Actualizar:

Tomado de fill-arrays-with-undefined

Esta parece ser una forma más optimizada de crear la matriz inicial, también la he actualizado para usar la función de mapa de segundo parámetro sugerida por @ felix-eve.

Array.from({ length: 3 }, (x, i) => {
  something();
});
nverba
fuente
3
Debo advertir esto diciendo que esto está bien si solo está escribiendo rápidamente algo, pero el rendimiento es horrible, así que probablemente no lo use para recursividad intensiva o en producción.
nverba
Si va por ES6, puede usar map () en lugar de forEach ()
Andy Ford
3
Si la concisión es el objetivo (y en realidad, incluso si no lo es), pase la función en lugar de llamarla:Array.from(Array(3)).forEach(something)
kvsm
1
También funciona con la representación de expresiones de reacción.
Josh Sharkey
4
Array.from()tiene un segundo parámetro opcional mapFn, que le permite ejecutar una función de mapa en cada elemento de la matriz, por lo que no es necesario usar forEach. Simplemente puede hacer:Array.from({length: 3}, () => somthing() )
Felix Eve
18

Como mencionas el subrayado:

Suponiendo que fes la función que desea llamar:

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

hará el truco. Por ejemplo, con f = function (x) { console.log(x); }, obtendrá en su consola: 0 0 1 0 1 2

ggozad
fuente
De hecho, pensé que querías la separación.
ggozad
2
_(3).times(function(n){return n;});debería hacer el truco. Vea los documentos aquí.
Chip
18

Con lodash :

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

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

O si quieres hacer algo N veces :

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

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

Consulte este enlace para la lodashinstalación.

aunque
fuente
15

Crear una matriz y filltodos los artículos con undefinedlo que mapel método podría funcionar:

Array.fill no tiene soporte para IE

// run 5 times:
Array(5).fill().map((item, i)=>{ 
   console.log(i) // print index
})

Si desea que lo anterior sea más "declerativo", mi solución basada en opiniones actualmente sería:


Usando el bucle de la vieja escuela (inverso):

// run 5 times:
for( let i=5; i--; )
   console.log(i) 

O como un "mientras" declarativo :

vsync
fuente
1
Para estar tranquilo, ejecuté una función uuid 50k veces para asegurarme de que nunca duplicara un uuid. Así que perfilé el bucle superior frente al inferior solo por diversión, simplemente ejecutándome en medio de una carga de página normal usando herramientas de desarrollo de Chrome, si no soy tonto, creo que se compara con ~ 1.200 millones con Array.indexOf () más la generación de 50k uuids. newschool = 1st-5561.2ms 2nd-5426.8ms | oldschool = 1st-4966.3ms / 2nd-4929.0ms Moraleja de la historia si no estás en el rango de más de mil millones, nunca notarías una diferencia ejecutando estas 200, 1k, incluso 10k veces para hacer algo. Supuse que alguien podría tener curiosidad como yo.
rifi2k
Eso es correcto y se sabe desde hace muchos años. Los diferentes enfoques no se presentaron para obtener beneficios de velocidad, sino para la compatibilidad con navegadores más antiguos.
vsync
3
Curiosamente, todos los que leen este hilo saben que no presentó los ejemplos para comparar su velocidad. Por casualidad, los usé para hacer una pequeña prueba y pensé que compartiría información que alguien en el camino podría encontrar interesante. Realmente no estoy en lo correcto porque en realidad no estaba respondiendo una pregunta, solo mostrando información y dando un recordatorio para que no se preocupe por la velocidad de un bucle cuando solo estás haciendo algunas cosas que terminarán en un par de ms de todos modos. Tampoco se sabe realmente porque la misma prueba de hace un año en ie puede encontrar una 50% más lenta porque los navegadores cambian todo el tiempo.
rifi2k
10

También puede hacer lo mismo con la desestructuración de la siguiente manera

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

o si necesitas index

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

Si no puede usar Underscorejs, puede implementarlo usted mismo. Al adjuntar nuevos métodos a los prototipos de números y cadenas, puede hacerlo así (usando las funciones de flecha de 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) );

Simplemente tiene que crear una expresión de función (de cualquier nombre) y asignarla a cualquier nombre de propiedad (en los prototipos) al que le gustaría acceder 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
fuente
1
Tendría que volver a investigar qué tan malo es parchear el prototipo, pero generalmente está bien
Alexander Mills
2

Hay una biblioteca fantástica llamada Ramda, que es similar a Underscore y Lodash, pero es más poderosa.

const R = require('ramda');

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

Ramda contiene muchas funciones útiles. Ver la documentación de Ramda

Jan Bodnar
fuente
Me encanta esta biblioteca como una solución FP moderna y elegante.
momocow
1

Puede usar la longitud de la matriz para ejecutar varias veces su tarea.

var arr = [1,2,3];

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

o

 var arr = [1,2,3];

 do
 {


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

Puedes llamarlo así:

times(3,4);
times(1,2,3,4);
times(1,3,5,7,9);
XU3352
fuente
+1: utiliza la capacidad nativa de JavaScript para llamar a funciones con cantidades variables de parámetros. No se necesita biblioteca adicional. Buena solución
RustyTheBoyRobot
1
// calls doSomething 42 times
Array( 42 ).join( "x" ).split( "" ).forEach( doSomething );

y

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

o (a través de https://stackoverflow.com/a/20066663/275501 )

Array.apply(null, {length: 42}).forEach( doSomething );
goofballLogic
fuente
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
  }
});

O

var x = [1,2,3];

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

EDITAR

Puede hacer lo siguiente con JS puro:

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

Solo use un bucle anidado (tal vez incluido en una función)

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

Entonces llámalo así:

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

Estas respuestas son todas buenas y están bien y, en mi opinión, @Andreas es la mejor, pero muchas veces en JS tenemos que hacer las cosas de forma asincrónica, en ese caso, async lo tiene cubierto:

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

Estas características de 'tiempos' no son muy útiles para la mayoría de los códigos de aplicaciones, pero deberían ser útiles para las pruebas.

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

loop(something, 3)
Goro
fuente
0

Dada una función something:

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

Y un nuevo método timesagregado al Arrayprototipo:

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

Este código:

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

Produce esto:

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

Lo que creo que responde a su pregunta actualizada (5 años después), pero me pregunto qué tan útil es que esto funcione en un arreglo ¿No sería el efecto el mismo que llamar [6].times(something), que a su vez podría escribirse como:

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

(aunque el uso de _como una variable basura probablemente golpeará lodash o subrayará si lo está usando)

pix
fuente
1
Se considera una mala práctica agregar métodos personalizados a un objeto JS nativo.
Lior Elrom
Puede usar letas in for (let _ of Array(6)) something()para evitar que lodash golpee fuera de la por al menos.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
0

Matriz. De (ES6)

function doSomthing() {
    ...
}

Úselo así:

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

O

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

O

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

Usando Array.fromy .forEach.

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

SeregPie
fuente
0

Suponiendo que podemos usar alguna sintaxis de ES6 como el operador de propagación, querremos hacer algo tantas veces como la suma de todos los números de la colección.

En este caso, si tiempos es igual a [1,2,3], el número total de veces será 6, es decir, 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 publicación debería ser útil si la lógica detrás de la construcción y distribución de una matriz no es evidente.

IliasT
fuente
0

Implementación de TypeScript:

Para aquellos de ustedes que estén interesados ​​en cómo implementar String.timesy Number.timesde una manera segura de escribir y que funcione con el thisArg, aquí tiene:

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(() => {})
}

Aquí se puede encontrar un enlace a TypeScript Playground con algunos ejemplos.

Esta publicación implementa soluciones publicadas por: Andreas Bergström , vinyll , Ozay Duman y SeregPie

Noah Anderson
fuente