mache <something> N mal (deklarative Syntax)

89

Gibt es in Javascript eine Möglichkeit, so etwas einfach zu schreiben:

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

Gibt es eine Bibliothek, die möglicherweise eine ähnliche Syntax unterstützt?

Update: zur Verdeutlichung - Ich möchte something()für jede Array-Element-Iteration 1,2- bzw. 3-mal aufgerufen werden

BreakPhreak
quelle
1
Ich würde sagen, dass es in JS keine solche Funktion gibt, und es ist eine der Top 5 fehlenden Funktionen. Es ist vor allem zum Testen von Software sehr nützlich.
Alexander Mills

Antworten:

46

Diese Antwort basiert Array.forEachohne Bibliothek auf nativer Vanille .

Um im Grunde something()dreimal anzurufen , verwenden Sie:

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

unter Berücksichtigung der folgenden Funktion:

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

Der Auslauf wird sein

something
something
something

Um diese Fragen zu beantworten, haben Sie folgende Möglichkeiten, something()1, 2 bzw. 3 Mal anzurufen :

Es ist 2017, Sie können ES6 verwenden:

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

oder im guten alten ES5:

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

In beiden Fällen wird der Auslauf sein

Der Auslauf wird sein

something

something
something

something
something
something

(einmal, dann zweimal, dann dreimal)

vinyll
quelle
18
Dies ist falsch, da es diesen Teil der Frage nicht erfüllt: "Ich möchte, dass etwas () 1,2- und 3-mal aufgerufen wird". Die Verwendung dieses Codes somethingwird nur dreimal aufgerufen, er sollte sechsmal aufgerufen werden.
Ian Newson
Dann denke ich, dass es als beste Antwort ausgewählt wurde , da es ein guter sauberer Start sein kann.
Vinyll
3
Sie können auch [...Array(i)]oder verwenden Array(i).fill(), je nach Ihren Anforderungen für die tatsächlichen Indizes.
Guido Bouman
Wenn Sie nicht an übergebenen Argumenten interessiert sind, verwenden Sie.forEach(something)
kvsm
83

Verwenden Sie einfach eine Schleife:

var times = 10;
for(var i=0; i < times; i++){
    doSomething();
}
ahren
quelle
3
Danke! Ich möchte von einer deklarativen Syntax profitieren (genau wie Jasmine usw.)
BreakPhreak
Alexander Mills
68

Mögliche ES6-Alternative.

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

Und wenn Sie möchten, dass es "1,2- bzw. 3-mal aufgerufen wird".

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

Aktualisieren:

Entnommen aus dem Füllen von Arrays mit undefinierten

Dies scheint eine optimierte Methode zum Erstellen des anfänglichen Arrays zu sein. Ich habe dies auch aktualisiert, um die von @ felix-eve vorgeschlagene zweite Parameterzuordnungsfunktion zu verwenden.

Array.from({ length: 3 }, (x, i) => {
  something();
});
nverba
quelle
3
Ich sollte dies einschränken, indem ich sage, dass dies in Ordnung ist, wenn Sie nur schnell etwas schreiben, aber die Leistung schrecklich ist. Verwenden Sie es also wahrscheinlich nicht für intensive Rekursionen oder in der Produktion.
Nverba
Wenn Sie sich für ES6 entscheiden, können Sie map () anstelle von forEach () verwenden
Andy Ford
3
Wenn Knappheit das Ziel ist (und tatsächlich, auch wenn dies nicht der Fall ist), übergeben Sie die Funktion, anstatt sie aufzurufen:Array.from(Array(3)).forEach(something)
kvsm
1
Funktioniert auch mit dem Rendern von Reaktionsausdrücken.
Josh Sharkey
3
Array.from()verfügt über einen optionalen zweiten Parameter mapFn, mit dem Sie eine Zuordnungsfunktion für jedes Element des Arrays ausführen können, sodass forEach nicht verwendet werden muss. Sie können einfach tun:Array.from({length: 3}, () => somthing() )
Felix Eve
19

Da Sie Underscore erwähnen:

Angenommen, fSie möchten die Funktion aufrufen:

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

wird den Trick machen. Zum Beispiel mit erhalten f = function (x) { console.log(x); }Sie auf Ihrer Konsole: 0 0 1 0 1 2

ggozad
quelle
In der Tat dachte ich, Sie wollten Trennung.
Ggozad
2
_(3).times(function(n){return n;});sollte den Trick machen. Siehe die Dokumente hier.
Chip
18

Mit lodash :

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

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

Oder wenn Sie etwas N-mal machen wollen :

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

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

Siehe diesen Link für die lodashInstallation

Tho
quelle
12

Erstellen Sie ein Array und fillalle Einzelteile mit undefinedso mapMethode funktionieren könnte:

Array.fill hat keine IE-Unterstützung

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


Verwenden der Old-School-Reveres-Schleife:

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

vsync
quelle
Ich habe eine UUID-Funktion 50.000 Mal ausgeführt , um sicherzustellen, dass sie niemals eine UUID dupliziert. Also habe ich die obere und die untere Schleife nur für Kicks profiliert und bin mitten in einer normalen Seitenladung mit Chrome Dev Tools gelaufen, wenn ich nicht dumm bin. Ich denke, ihre ~ 1,2 Milliarden vergleichen sich mit Array.indexOf () und generieren 50.000 UUids. newschool = 1st-5561.2ms 2nd-5426.8ms | oldschool = 1st-4966.3ms / 2nd-4929.0ms Moral der Geschichte, wenn du nicht im Milliardenbereich bist, würdest du niemals einen Unterschied bemerken, wenn du diese 200, 1k oder sogar 10k mal laufen lässt, um etwas zu tun. Ich dachte, jemand könnte neugierig sein wie ich.
Rifi2k
Das ist richtig und seit vielen Jahren bekannt. Die verschiedenen Ansätze wurden nicht für Geschwindigkeitsvorteile vorgestellt, sondern für die Unterstützung älterer Browser.
vsync
2
Natürlich weiß jeder, der diesen Thread liest, dass Sie die Beispiele nicht vorgestellt haben, um ihre Geschwindigkeit zu vergleichen. Ich habe sie zufällig benutzt, um einen kleinen Test durchzuführen, und dachte mir, ich würde einige Informationen teilen, die jemand auf der Straße interessant finden könnte. Ich bin nicht wirklich richtig, weil ich eine Frage nicht wirklich beantwortet habe, nur Informationen angezeigt und daran erinnert habe, die Geschwindigkeit einer Schleife nicht zu schwitzen, wenn Sie nur ein paar Dinge tun, die sowieso in ein paar ms enden. Es ist auch nicht wirklich bekannt, weil der gleiche Test vor einem Jahr in dh 50% langsamer ist, weil sich die Browser ständig ändern.
Rifi2k
9

Mit der Destrukturierung können Sie auch Folgendes tun

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

oder wenn Sie Index benötigen

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

Wenn Sie Underscorejs nicht verwenden können, können Sie es selbst implementieren. Wenn Sie den Prototypen Number und String neue Methoden hinzufügen, können Sie dies folgendermaßen tun (mithilfe der ES6-Pfeilfunktionen):

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

Sie müssen lediglich einen Funktionsausdruck (mit einem beliebigen Namen) erstellen und ihn einem beliebigen Eigenschaftsnamen (auf den Prototypen) zuweisen, auf den Sie zugreifen möchten:

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
quelle
1
Ich müsste noch einmal untersuchen, wie schlimm es ist, den Prototyp zu patchen, aber normalerweise ist es in Ordnung
Alexander Mills
2

Es gibt eine fantastische Bibliothek namens Ramda, die Underscore und Lodash ähnelt, aber leistungsfähiger ist.

const R = require('ramda');

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

Ramda enthält viele nützliche Funktionen. Siehe Ramda-Dokumentation

Jan Bodnar
quelle
Ich liebe diese Bibliothek als moderne und elegante FP-Lösung.
Momocow
1

Sie können die Länge des Arrays verwenden, um die Häufigkeit Ihrer Aufgabe auszuführen.

var arr = [1,2,3];

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

oder

 var arr = [1,2,3];

 do
 {


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

Sie können es so nennen:

times(3,4);
times(1,2,3,4);
times(1,3,5,7,9);
XU3352
quelle
+1 - Dies nutzt die native JavaScript-Fähigkeit, Funktionen mit variablen Mengen an Parametern aufzurufen. Keine zusätzliche Bibliothek erforderlich. Schöne Lösung
RustyTheBoyRobot
1
// calls doSomething 42 times
Array( 42 ).join( "x" ).split( "" ).forEach( doSomething );

und

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

oder (über https://stackoverflow.com/a/20066663/275501 )

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

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

Verwenden von jQuery .each()

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

ODER

var x = [1,2,3];

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

BEARBEITEN

Mit reinem JS können Sie wie folgt vorgehen:

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

Verwenden Sie einfach eine verschachtelte Schleife (möglicherweise in einer Funktion eingeschlossen).

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

Dann nenne es einfach so:

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

Diese Antworten sind alle gut und gut und IMO @Andreas ist das Beste, aber in JS müssen wir oft asynchron vorgehen. In diesem Fall hat async Sie behandelt:

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

Diese 'Zeiten'-Funktionen sind für die meisten Anwendungscodes nicht sehr nützlich, sollten jedoch zum Testen nützlich sein.

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

loop(something, 3)
Goro
quelle
0

Gegeben eine Funktion something:

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

Und eine neue Methode timeszum ArrayPrototyp hinzugefügt :

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

Dieser Code:

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

Gibt dies aus:

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

Was meiner Meinung nach Ihre aktualisierte Frage beantwortet (5 Jahre später), aber ich frage mich, wie nützlich es ist, diese Arbeit auf einem Array zu haben? Wäre der Effekt nicht der gleiche wie beim Aufrufen [6].times(something), der wiederum wie folgt geschrieben werden könnte:

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

(obwohl die Verwendung _als Junk-Variable wahrscheinlich zu Lodash oder Unterstrichen führen wird, wenn Sie sie verwenden)

pix
quelle
1
Es wird als schlechte Praxis angesehen, einem nativen JS-Objekt benutzerdefinierte Methoden hinzuzufügen.
Lior Elrom
Sie können letwie in verwenden for (let _ of Array(6)) something(), um zu verhindern, dass zumindest außerhalb des Lodashs ein Überfall auftritt.
Ciro Santilli 4 冠状 病 六四.
0

Array.from (ES6)

function doSomthing() {
    ...
}

Verwenden Sie es so:

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

Oder

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

Oder

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

Verwenden von Array.fromund .forEach.

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

SeregPie
quelle
0

Angenommen, wir können eine ES6-Syntax wie den Spread-Operator verwenden, möchten wir etwas so oft tun wie die Summe aller Zahlen in der Sammlung.

In diesem Fall beträgt [1,2,3]die Gesamtzahl der Zeiten 6, dh 1 + 2 + 3, wenn die Zeiten gleich sind .

/**
 * @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

Dieser Beitrag sollte hilfreich sein, wenn die Logik hinter dem Erstellen und Verbreiten eines Arrays nicht offensichtlich ist.

IliasT
quelle
0

TypeScript-Implementierung:

Für diejenigen unter Ihnen, die daran interessiert sind, wie sie implementiert werden sollen String.timesund Number.timesauf eine Art und Weise, die typsicher ist und mit dem funktioniert thisArg, hier geht's:

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

Einen Link zum TypeScript-Spielplatz mit einigen Beispielen finden Sie hier

Dieser Beitrag implementiert Lösungen von: Andreas Bergström , vinyll , Ozay Duman & SeregPie

Noah Anderson
quelle