faire <quelque chose> N fois (syntaxe déclarative)

90

Existe-t-il un moyen en Javascript d'écrire facilement quelque chose comme ça:

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

Une bibliothèque qui pourrait prendre en charge une syntaxe similaire peut-être?

Mise à jour: pour clarifier - je voudrais something()être appelé 1, 2 et 3 fois respectivement pour chaque itération d'élément de tableau

BreakPhreak
la source
1
Je dirais qu'il n'y a pas de fonctionnalité comme celle-ci dans JS, et que c'est l'une des 5 principales fonctionnalités manquantes. C'est très utile pour tester des logiciels, plus que tout.
Alexander Mills

Réponses:

46

Cette réponse est basée sur Array.forEach, sans aucune bibliothèque, juste de la vanille native .

Pour appeler essentiellement something()3 fois, utilisez:

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

considérant la fonction suivante:

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

L'outpout sera

something
something
something

Pour répondre à ces questions, voici une façon d'appeler respectivement something()1, 2 et 3 fois:

Nous sommes en 2017, vous pouvez utiliser ES6:

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

ou dans le bon vieux ES5:

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

Dans les deux cas, la sortie sera

L'outpout sera

something

something
something

something
something
something

(une, puis deux, puis 3 fois)

vinyll
la source
18
Ceci est incorrect car cela ne répond pas à cette partie de la question: «Je voudrais que quelque chose () soit appelé 1, 2 et 3 fois». L'utilisation de ce code somethingn'est appelée que 3 fois, il devrait être appelé 6 fois.
Ian Newson
Ensuite, je suppose que cela a été choisi comme meilleure réponse car cela peut être un bon départ plus propre.
vinyll
3
Vous pouvez également utiliser [...Array(i)]ou Array(i).fill(), en fonction de vos besoins pour les index réels.
Guido Bouman le
Si vous n'êtes pas intéressé par les arguments passés, utilisez.forEach(something)
kvsm
85

Utilisez simplement une boucle:

var times = 10;
for(var i=0; i < times; i++){
    doSomething();
}
Ahren
la source
3
Merci! Je souhaite bénéficier d'une syntaxe déclarative (tout comme Jasmine etc)
BreakPhreak
à droite, mais une syntaxe déclarative de boucle for fonctionnelle serait également bien meilleure
Alexander Mills
69

Alternative ES6 possible.

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

Et, si vous voulez qu'il "soit appelé respectivement 1, 2 et 3 fois".

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

Mettre à jour:

Pris à partir de remplissage-tableaux-avec-undefined

Cela semble être une façon plus optimisée de créer le tableau initial, j'ai également mis à jour cela pour utiliser la deuxième fonction de mappage de paramètres suggérée par @ felix-eve.

Array.from({ length: 3 }, (x, i) => {
  something();
});
nverba
la source
3
Je devrais mettre cela en garde en disant que ce n'est pas grave si vous écrivez rapidement quelque chose, mais les performances sont horribles, alors ne l'utilisez pas du tout pour une récursivité intensive ou en production probablement.
nverba
Si vous optez pour ES6, vous pouvez utiliser map () au lieu de forEach ()
Andy Ford
3
Si l'objectif est la concision (et en fait, même si ce n'est pas le cas), passez la fonction au lieu de l'appeler:Array.from(Array(3)).forEach(something)
kvsm
1
Fonctionne également avec le rendu d'expression de réaction.
Josh Sharkey
4
Array.from()a un deuxième paramètre facultatif mapFn, qui vous permet d'exécuter une fonction de carte sur chaque élément du tableau, il n'est donc pas nécessaire d'utiliser forEach. Vous pouvez simplement faire:Array.from({length: 3}, () => somthing() )
Felix Eve
19

Depuis que vous mentionnez Underscore:

En supposant que fla fonction que vous souhaitez appeler est:

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

fera l'affaire. Par exemple, avec f = function (x) { console.log(x); }, vous obtiendrez sur votre console: 0 0 1 0 1 2

ggozad
la source
En effet, je pensais que vous vouliez la séparation.
ggozad
2
_(3).times(function(n){return n;});devrait faire l'affaire. Consultez la documentation ici.
Puce du
18

Avec lodash :

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

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

Ou si vous voulez faire quelque chose N fois :

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

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

Reportez-vous à ce lien pour l' lodashinstallation

Tho
la source
12

Créez un tableau et filltous les éléments avec la méthode undefinedso mappourrait fonctionner:

Array.fill n'a pas de support IE

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


Utilisation de la boucle de révères à l'ancienne:

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

vsync
la source
Par souci de tranquillité, j'ai exécuté une fonction uuid 50 000 fois pour m'assurer qu'elle ne dupliquait jamais un uuid. J'ai donc profilé la boucle du haut par rapport au bas juste pour les coups de pied, juste en cours d'exécution au milieu d'un chargement de page normal à l'aide des outils de développement de chrome si je ne suis pas stupide, je pense que son ~ 1,2 milliard se compare à l'aide de Array.indexOf () plus la génération de 50k uuids. école de nouvelles = 1er-5561,2 ms 2e-5426,8 ms | oldschool = 1st-4966.3ms / 2nd-4929.0ms Morale de l'histoire si vous n'êtes pas dans la fourchette du milliard +, vous ne remarquerez jamais une différence en exécutant ces 200, 1k, voire 10k fois pour faire quelque chose. Je pensais que quelqu'un pouvait être curieux comme moi.
rifi2k
C'est correct et connu depuis de nombreuses années. Les différentes approches n'ont pas été présentées pour les avantages de vitesse mais pour la prise en charge des navigateurs plus anciens.
vsync
2
De toute évidence, tout le monde qui lit ce fil sait que vous n'avez pas présenté les exemples pour comparer leur vitesse. Il m'est arrivé de les utiliser pour faire un petit test et j'ai pensé que je partagerais des informations que quelqu'un sur la route pourrait trouver intéressantes. Je n'ai pas vraiment raison car je ne répondais pas vraiment à une question en affichant simplement des informations et en me rappelant de ne pas transpirer la vitesse d'une boucle lorsque vous ne faites que quelques choses qui se termineront de toute façon dans quelques ms. Ce n'est pas vraiment connu non plus car le même test il y a un an en ie peut en trouver un 50% plus lent car les navigateurs changent tout le temps.
rifi2k
9

Vous pouvez également faire la même chose avec la déstructuration comme suit

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

ou si vous avez besoin d'index

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

Si vous ne pouvez pas utiliser Underscorejs, vous pouvez l'implémenter vous-même. En attachant de nouvelles méthodes aux prototypes Number et String, vous pouvez le faire comme ceci (en utilisant les fonctions fléchées 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) );

Vous devez simplement créer une expression de fonction (quel que soit le nom) et l'assigner au nom de propriété (sur les prototypes) auquel vous souhaitez accéder en tant que:

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
la source
1
Je devrais réexaminer à quel point il est mauvais de patcher le prototype, mais généralement c'est très bien
Alexander Mills
2

Il existe une bibliothèque fantastique appelée Ramda, qui est similaire à Underscore et Lodash, mais qui est plus puissante.

const R = require('ramda');

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

Ramda contient de nombreuses fonctions utiles. Voir la documentation Ramda

Jan Bodnar
la source
J'adore cette bibliothèque en tant que solution FP moderne et élégante.
momocow
1

Vous pouvez utiliser la longueur du tableau pour exécuter le nombre de fois votre tâche.

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
la source
1
times = function () {
    var length = arguments.length;
    for (var i = 0; i < length ; i++) {
        for (var j = 0; j < arguments[i]; j++) {
            dosomthing();
        }
    }
}

Vous pouvez l'appeler comme ceci:

times(3,4);
times(1,2,3,4);
times(1,3,5,7,9);
XU3352
la source
+1 - Cela utilise la capacité JavaScript native pour appeler des fonctions avec des quantités variables de paramètres. Aucune bibliothèque supplémentaire nécessaire. Nice solution
RustyTheBoyRobot
1
// calls doSomething 42 times
Array( 42 ).join( "x" ).split( "" ).forEach( doSomething );

et

// 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
la source
1
var times = [1,2,3];

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

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

ÉDITER

Vous pouvez faire comme ci-dessous avec pur JS:

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

Utilisez simplement une boucle imbriquée (peut-être incluse dans une fonction)

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

Alors appelez-le comme ceci:

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

Ces réponses sont toutes bonnes et bien et IMO @Andreas est la meilleure, mais plusieurs fois dans JS, nous devons faire les choses de manière asynchrone, dans ce cas, async vous a couvert:

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

Ces fonctionnalités «temps» ne sont pas très utiles pour la plupart des codes d'application, mais devraient être utiles pour les tests.

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

loop(something, 3)
Goro
la source
0

Étant donné une fonction something:

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

Et une nouvelle méthode timesajoutée au Arrayprototype:

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

Ce code:

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

Sort ceci:

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

Ce qui, je pense, répond à votre question mise à jour (5 ans plus tard) mais je me demande à quel point il est utile d'avoir ce travail sur un tableau? L'effet ne serait-il pas le même que celui d'appeler [6].times(something), qui à son tour pourrait s'écrire:

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

(bien que l'utilisation de _comme variable indésirable réduira probablement le lodash ou le soulignement si vous l'utilisez)

pix
la source
1
Il est considéré comme une mauvaise pratique d'ajouter des méthodes personnalisées à un objet JS natif.
Lior Elrom
Vous pouvez utiliser letcomme dans for (let _ of Array(6)) something()pour éviter le clobbering lodash en dehors de la pendant au moins.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
0

Tableau. De (ES6)

function doSomthing() {
    ...
}

Utilisez-le comme ceci:

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
la source
0

Utilisation Array.fromet .forEach.

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

SeregPie
la source
0

En supposant que nous pouvons utiliser une syntaxe ES6 comme l'opérateur de propagation, nous voudrons faire quelque chose autant de fois que la somme de tous les nombres de la collection.

Dans ce cas, si temps est égal à [1,2,3], le nombre total de fois sera de 6, soit 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

Cet article devrait être utile si la logique derrière la construction et la diffusion d'un tableau n'est pas apparente.

IliasT
la source
0

Implémentation TypeScript:

Pour ceux d'entre vous qui sont intéressés par la façon de mettre en œuvre String.timeset Number.timesd'une manière qui est sûre de type et fonctionne avec le thisArg, voilà:

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

Un lien vers le TypeScript Playground avec quelques exemples peut être trouvé ici

Cet article met en œuvre des solutions postées par: Andreas Bergström , vinyll , Ozay Duman et SeregPie

Noah Anderson
la source