<something> N kere yap (bildirim temelli sözdizimi)


97

Javascript'te böyle bir şeyi kolayca yazmanın bir yolu var mı:

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

Bazı benzer sözdizimini destekleyebilecek herhangi bir kitaplık olabilir mi?

Güncelleme: açıklığa kavuşturmak için - something()Her dizi öğesi yinelemesi için sırasıyla 1,2 ve 3 kez çağrılmak istiyorum


2
JS'de böyle bir özellik olmadığını söyleyebilirim ve bu eksik olan ilk 5 özelliktir. Her şeyden çok yazılımı test etmek için çok kullanışlıdır.
Alexander Mills

Yanıtlar:


48

Bu cevap Array.forEach, herhangi bir kitaplık olmadan sadece yerel vanilyaya dayanmaktadır .

Temelde something()3 kez aramak için şunu kullanın:

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

aşağıdaki işlevi dikkate alarak:

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

Çıkış olacak

something
something
something

Bu soruyu tamamlamak için something(), sırasıyla 1, 2 ve 3 kez arama yapmanın bir yolu :

Yıl 2017, ES6 kullanabilirsiniz:

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

veya eski güzel ES5'te:

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

Her iki durumda da çıkış

Çıkış olacak

something

something
something

something
something
something

(bir kez, sonra iki kez, sonra 3 kez)


18
Bu yanlıştır çünkü sorunun şu kısmını karşılamıyor: 'Bir şeyin () 1,2 ve 3 kez çağrılmasını istiyorum'. Bu kodu kullanmak somethingsadece 3 defa aranır, 6 defa aranmalıdır.
Ian Newson

Daha temiz bir başlangıç ​​olabileceği için en iyi cevap olarak seçildi .
vinyll

3
Ayrıca , gerçek dizinler için ihtiyaçlarınıza bağlı olarak [...Array(i)]veya kullanabilirsiniz Array(i).fill().
Guido Bouman

.forEach(something)
Geçilen

88

Sadece bir döngü kullanın:

var times = 10;
for(var i=0; i < times; i++){
    doSomething();
}

3
teşekkür ederim! Bildirime dayalı bir sözdiziminden yararlanmak istiyorum (tıpkı Jasmine vb. Gibi)
BreakPhreak

doğru, ancak işlevsel bir for-loop bildirimsel sözdizimi de çok daha iyi olurdu
Alexander Mills

73

Olası ES6 alternatifi.

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

Ve eğer "sırasıyla 1,2 ve 3 kez çağrılmasını" istiyorsanız.

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

Güncelleme:

Alındığı dolum-diziler-ile-tanımsız

Bu, ilk diziyi oluşturmanın daha optimize bir yolu gibi görünüyor, bunu ayrıca @ felix-eve tarafından önerilen ikinci parametre eşleme işlevini kullanmak için güncelledim.

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

3
Bir şeyi hızlı bir şekilde yazıyorsanız, bunun sorun olmadığını söyleyerek bunu uyarmalıyım, ancak performans korkunç, bu yüzden muhtemelen yoğun özyineleme için veya üretimde kullanmayın.
nverba

ES6 için gidiyorsanız, forEach () yerine map () kullanabilirsiniz
Andy Ford

3
Hedef kısalıksa (ve aslında öyle olmasa bile), fonksiyonu çağırmak yerine geç:Array.from(Array(3)).forEach(something)
kvsm

1
React ifade oluşturma ile de çalışır.
Josh Sharkey

4
Array.from()mapFndizinin her bir öğesi üzerinde bir eşleme işlevi çalıştırmanıza izin veren isteğe bağlı bir ikinci parametreye sahiptir , bu nedenle forEach'i kullanmaya gerek yoktur. Şunları yapabilirsiniz:Array.from({length: 3}, () => somthing() )
Felix Eve

18

Alt Çizgi'den bahsettiğinizden beri:

Çağırmak fistediğiniz işlevin:

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

hile yapacak. Örneğin, ile f = function (x) { console.log(x); }konsolunuza gireceksiniz: 0 0 1 0 1 2


Doğrusu, ayrılmak istediğini sanıyordum.
ggozad

2
_(3).times(function(n){return n;});hile yapmalı. Dokümanlara buradan bakın.
Chip

18

İle lodash :

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

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

Veya bir şeyi N kez yapmak istiyorsanız :

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

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

Bakın bu bağlantı için lodashkurulum


15

Bir Dizi oluşturun ve bu yöntemle filltüm öğeler çalışabilir:undefinedmap

Array.fill IE desteği yok

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

Yukarıdakileri daha "belirleyici" hale getirmek istiyorsanız, şu anda görüşe dayalı çözümüm şöyle olacaktır:


Eski usul (ters) döngüsünü kullanma:

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

Veya bildirimsel "while" olarak :


1
Aklın bir parçası olarak, bir uuid'i asla çoğaltmadığından emin olmak için 50k kez bir uuid işlevi çalıştırdım . Bu yüzden, sadece tekmeler için üst döngü ile alt arasında profil oluşturdum, eğer aptal değilsem, krom geliştirme araçlarını kullanarak normal bir sayfa yüklemesinin ortasında koşarak, bence ~ 1.2 milyar Array.indexOf () artı 50k uuids üretiyor. haber okulu = 1st-5561.2ms 2.-5426.8ms | oldschool = 1st-4966.3ms / 2nd-4929.0ms Hikayenin ahlaki eğer u milyar + aralıkta değilseniz u bir şeyler yapmak için bu 200, 1k, hatta 10k kez koşarken bir fark asla fark etmezsiniz . Birinin benim gibi meraklı olabileceğini düşündüm.
rifi2k

Bu doğru ve yıllardır biliniyor. Farklı yaklaşımlar hız avantajları için değil, eski tarayıcıların desteği için sunuldu.
vsync

3
Açıkçası, bu konuyu okuyan herkes, hızlarını karşılaştırmak için örnekleri sunmadığınızı biliyor. Onları küçük bir test yapmak için kullandım ve yolun aşağısındaki birinin ilginç bulabileceği bazı bilgileri paylaşacağımı düşündüm. Gerçekten doğru değilim, çünkü bir soruyu gerçekten cevaplamıyordum ve sadece birkaç ms ile bitecek birkaç şeyi yaptığınızda bir döngünün hızını terlememek için bir hatırlatma vermiyordum. Aslında bilinmemektedir çünkü bir yıl önce ie'deki aynı test% 50 daha yavaş bulabilir çünkü tarayıcılar her zaman değişir.
rifi2k

10

Aynı şeyi yıkarak da aşağıdaki gibi yapabilirsiniz

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

veya dizine ihtiyacınız varsa

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

8

Underscorejs kullanamıyorsanız, kendiniz uygulayabilirsiniz. Number ve String prototiplerine yeni yöntemler ekleyerek, bunu şu şekilde yapabilirsiniz (ES6 ok işlevlerini kullanarak):

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

Sadece bir işlev ifadesi (adı ne olursa olsun) oluşturmanız ve ona erişmek istediğiniz özellik adına (prototiplerde) atamanız gerekir:

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;

1
Prototipi yamamanın ne kadar kötü olduğunu yeniden araştırmam gerekecek, ama genellikle sorun değil
Alexander Mills

2

Underscore ve Lodash'a benzeyen, ancak daha güçlü olan Ramda adlı fantastik bir kütüphane var.

const R = require('ramda');

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

Ramda pek çok kullanışlı işlev içerir. Ramda belgelerine bakın


Bu kütüphaneyi modern ve zarif bir FP çözümü olarak seviyorum.
momocow

1

Görevinizi kaç kez yürütmek için dizi uzunluğunu kullanabilirsiniz.

var arr = [1,2,3];

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

veya

 var arr = [1,2,3];

 do
 {


 }
 while (i++ < arr.length);


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

Şöyle diyebilirsiniz:

times(3,4);
times(1,2,3,4);
times(1,3,5,7,9);

+1 - Bu, değişken miktarlarda parametrelere sahip işlevleri çağırmak için yerel JavaScript yeteneğini kullanır. Ek kitaplığa gerek yok. Güzel çözüm
RustyTheBoyRobot

1
// calls doSomething 42 times
Array( 42 ).join( "x" ).split( "" ).forEach( doSomething );

ve

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

veya ( https://stackoverflow.com/a/20066663/275501 aracılığıyla )

Array.apply(null, {length: 42}).forEach( doSomething );

1
var times = [1,2,3];

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

JQuery kullanma .each()

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

VEYA

var x = [1,2,3];

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

DÜZENLE

Saf JS ile aşağıdakileri yapabilirsiniz:

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

0

Yalnızca iç içe geçmiş bir döngü kullanın (bir işlevin içinde olabilir)

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

Öyleyse şöyle diyelim:

times( doSomething, [1,2,3] );

0

Bu yanıtların hepsi iyi ve iyi ve IMO @Andreas en iyisidir, ancak JS'de çoğu zaman işleri eşzamansız olarak yapmak zorundayız, bu durumda eşzamansız olarak şunları kapsar:

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

Bu 'zamanlar' özellikleri, çoğu uygulama kodu için çok yararlı değildir, ancak test için yararlı olmalıdır.


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

loop(something, 3)

0

Bir işlev verildiğinde something:

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

Ve prototipe yeni bir yöntem timeseklendi Array:

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

Bu kod:

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

Şu çıktılar:

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

Bu yüzden bence güncellenmiş soru (5 yıl sonra) cevap ama bir dizide bu işi yapmak ne kadar yararlı acaba? Etkisi arama ile aynı olmaz mıydı ki bu [6].times(something)da şöyle yazılabilir:

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

( _Önemsiz değişken olarak kullanımı muhtemelen lodash'ı veya onu kullanıyorsanız altını çizecek olsa da)


1
Yerel bir JS nesnesine özel yöntemler eklemek kötü bir uygulama olarak kabul edilir.
Lior Elrom

En azından lodash'ın dışına çıkmayı önlemek için letin olarak kullanabilirsiniz for (let _ of Array(6)) something().
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

0

Array.from (ES6)

function doSomthing() {
    ...
}

Bunu şu şekilde kullanın:

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

Veya

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

Veya

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

0

Kullanılması Array.fromve .forEach.

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


0

Yayılma operatörü gibi bir ES6 sözdizimi kullanabileceğimizi varsayarsak, koleksiyondaki tüm sayıların toplamı kadar bir şey yapmak isteyeceğiz.

Bu durumda, zamanlar eşitse [1,2,3], toplam sefer sayısı 6 olacaktır, yani 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

Bu gönderi, bir dizi oluşturmanın ve yaymanın arkasındaki mantık açık değilse yardımcı olacaktır.


0

TypeScript Uygulaması:

Nasıl uygulanacağı String.timesve Number.timesgüvenli bir şekilde nasıl uygulanacağıyla ilgilenen ve onunla birlikte çalışanlarınız için thisArgişte:

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

TypeScript Playground'a bazı örneklerle bir bağlantı burada bulunabilir

Bu gönderi , Andreas Bergström , vinyll , Ozay Duman ve SeregPie tarafından yayınlanan çözümleri uygular.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.