lakukan <something> N kali (sintaks deklaratif)

97

Apakah ada cara di Javascript untuk menulis sesuatu seperti ini dengan mudah:

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

Pustaka apa saja yang mungkin mendukung sintaks serupa?

Perbarui: untuk memperjelas - Saya ingin something()dipanggil 1,2 dan 3 kali masing-masing untuk setiap iterasi elemen array

BreakPhreak
sumber
2
Saya akan mengatakan tidak ada fitur seperti ini di JS, dan ini adalah 5 fitur teratas yang hilang. Ini sangat berguna untuk menguji perangkat lunak, lebih dari apapun.
Alexander Mills

Jawaban:

48

Jawaban ini didasarkan pada Array.forEach, tanpa perpustakaan apa pun, hanya vanili asli .

Untuk menelepon something()3 kali pada dasarnya , gunakan:

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

mempertimbangkan fungsi berikut:

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

Pukulan keluar akan terjadi

something
something
something

Untuk menjawab pertanyaan ini, berikut adalah cara untuk melakukan panggilan something()1, 2 dan 3 kali masing-masing:

Ini 2017, Anda dapat menggunakan ES6:

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

atau di ES5 lama yang bagus:

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

Dalam kedua kasus, outpout akan terjadi

Pukulan keluar akan terjadi

something

something
something

something
something
something

(sekali, lalu dua kali, lalu 3 kali)

vinyll
sumber
18
Ini tidak benar karena tidak memenuhi bagian pertanyaan ini: 'Saya ingin sesuatu () dipanggil 1,2 dan 3 kali'. Menggunakan kode somethingini hanya dipanggil 3 kali, harus dipanggil 6 kali.
Ian Newson
Maka saya kira itu telah dipilih sebagai jawaban terbaik karena ini mungkin awal yang bersih dan baik.
vinyll
3
Anda juga dapat menggunakan [...Array(i)]atau Array(i).fill(), tergantung pada kebutuhan Anda akan indeks yang sebenarnya.
Guido Bouman
Jika Anda tidak tertarik dengan argumen yang diteruskan, gunakan.forEach(something)
kvsm
88

Cukup gunakan satu lingkaran:

var times = 10;
for(var i=0; i < times; i++){
    doSomething();
}
ahren
sumber
3
Terima kasih! Saya ingin mendapatkan keuntungan dari sintaks deklaratif (seperti Jasmine dll)
BreakPhreak
benar, tetapi sintaks deklaratif for-loop fungsional juga akan jauh lebih baik
Alexander Mills
74

Alternatif ES6 yang memungkinkan.

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

Dan, jika Anda ingin "dipanggil masing-masing 1,2 dan 3 kali".

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

Memperbarui:

Diambil dari filling-array-with-undefined

Ini tampaknya menjadi cara yang lebih dioptimalkan untuk membuat array awal, saya juga memperbarui ini untuk menggunakan fungsi peta parameter kedua yang disarankan oleh @ felix-eve.

Array.from({ length: 3 }, (x, i) => {
  something();
});
nverba
sumber
3
Saya harus mengingatkan ini dengan mengatakan ini tidak apa-apa jika Anda hanya membuat skrip dengan cepat, tetapi kinerjanya buruk, jadi jangan gunakan untuk rekursi intensif atau mungkin dalam produksi sama sekali.
nverba
Jika Anda menggunakan ES6, Anda dapat menggunakan map () daripada forEach ()
Andy Ford
3
Jika ketegasan adalah tujuannya (dan sebenarnya, meskipun tidak), teruskan fungsi alih-alih memanggilnya:Array.from(Array(3)).forEach(something)
kvsm
1
Bekerja dengan rendering ekspresi react juga.
Josh Sharkey
4
Array.from()memiliki parameter opsional kedua mapFn, yang memungkinkan Anda menjalankan fungsi peta pada setiap elemen array, jadi tidak perlu menggunakan forEach. Anda hanya dapat melakukan:Array.from({length: 3}, () => somthing() )
Felix Eve
18

Karena Anda menyebutkan Underscore:

Dengan asumsi fadalah fungsi yang ingin Anda panggil:

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

akan melakukan triknya. Misalnya, dengan f = function (x) { console.log(x); }, Anda akan mendapatkan konsol Anda: 0 0 1 0 1 2

ggozad.dll
sumber
Memang, saya pikir Anda ingin berpisah.
ggozad
2
_(3).times(function(n){return n;});harus melakukan triknya. Lihat dokumentasinya di sini.
Chip
18

Dengan lodash :

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

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

Atau jika Anda ingin melakukan sesuatu N kali :

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

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

Lihat tautan ini untuk lodashinstalasi

Tho
sumber
15

Buat Array dan fillsemua item dengan undefinedbegitu mapmetode bisa bekerja:

Array.fill tidak memiliki dukungan IE

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

Jika Anda ingin membuat yang di atas lebih "dekleratif", solusi berbasis opini saya saat ini adalah:


Menggunakan loop sekolah tua (terbalik):

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

Atau sebagai deklaratif "while" :

vsync
sumber
1
Sepenggal pikiran saya menjalankan fungsi uuid 50k kali untuk memastikan itu tidak pernah menduplikasi uuid. Jadi saya memprofilkan loop atas vs bawah hanya untuk iseng, hanya berjalan di tengah pemuatan halaman normal menggunakan alat chrome dev jika saya tidak bodoh, saya pikir ~ 1,2 miliar dibandingkan menggunakan Array.indexOf () ditambah menghasilkan 50k uuids. newschool = 1st-5561.2ms 2nd-5426.8ms | oldschool = 1st-4966.3ms / 2nd-4929.0ms Moral dari cerita ini jika Anda tidak berada dalam rentang miliar + Anda tidak akan pernah melihat perbedaan menjalankan 200, 1k, bahkan 10k kali ini untuk melakukan sesuatu. Kupikir seseorang mungkin penasaran seperti aku.
rifi2k
Itu benar dan telah dikenal selama bertahun-tahun. Pendekatan yang berbeda tidak disajikan untuk keuntungan kecepatan tetapi untuk dukungan browser lama.
vsync
3
Anehnya semua orang yang membaca utas ini, tahu Anda tidak memberikan contoh untuk membandingkan kecepatan mereka. Saya kebetulan menggunakannya untuk menjalankan tes kecil dan berpikir saya akan membagikan beberapa info yang mungkin menarik bagi seseorang di masa mendatang. Saya tidak benar-benar benar karena saya tidak benar-benar menjawab pertanyaan hanya menampilkan informasi dan memberikan pengingat untuk tidak memusingkan kecepatan loop ketika Anda hanya melakukan beberapa hal yang akan berakhir dalam beberapa ms. Itu juga tidak benar-benar diketahui karena pengujian yang sama tahun lalu di ie mungkin menemukan satu 50% lebih lambat karena browser berubah sepanjang waktu.
rifi2k
10

Anda juga dapat melakukan hal yang sama dengan merusak sebagai berikut

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

atau jika Anda membutuhkan index

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

Jika Anda tidak dapat menggunakan Underscorejs, Anda dapat menerapkannya sendiri. Dengan melampirkan metode baru ke prototipe Angka dan String, Anda dapat melakukannya seperti ini (menggunakan fungsi panah 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) );

Anda hanya perlu membuat ekspresi fungsi (dengan nama apa pun) dan menetapkannya ke nama properti apa pun (pada prototipe) yang ingin Anda akses sebagai:

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
sumber
1
Saya harus menyelidiki kembali seberapa buruk menambal prototipe, tetapi biasanya baik
Alexander Mills
2

Ada perpustakaan fantastis bernama Ramda, yang mirip dengan Underscore dan Lodash, tetapi lebih bertenaga.

const R = require('ramda');

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

Ramda mengandung banyak fungsi yang bermanfaat. Lihat dokumentasi Ramda

Jan Bodnar
sumber
Saya suka perpustakaan ini sebagai solusi FP yang modern dan elegan.
momocow
1

Anda dapat menggunakan length of array untuk mengeksekusi berapa kali tugas Anda.

var arr = [1,2,3];

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

atau

 var arr = [1,2,3];

 do
 {


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

Anda bisa menyebutnya seperti ini:

times(3,4);
times(1,2,3,4);
times(1,3,5,7,9);
XU3352
sumber
+1 - Ini menggunakan kemampuan JavaScript asli untuk memanggil fungsi dengan jumlah parameter yang bervariasi. Tidak perlu perpustakaan tambahan. Solusi bagus
RustyTheBoyRobot
1
// calls doSomething 42 times
Array( 42 ).join( "x" ).split( "" ).forEach( doSomething );

dan

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

atau (melalui https://stackoverflow.com/a/20066663/275501 )

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

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

Menggunakan jQuery .each()

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

ATAU

var x = [1,2,3];

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

EDIT

Anda dapat melakukan seperti di bawah ini dengan JS murni:

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

Cukup gunakan loop bersarang (mungkin tertutup dalam suatu fungsi)

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

Kemudian sebut saja seperti ini:

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

Semua jawaban ini bagus dan bagus dan IMO @Andreas adalah yang terbaik, tetapi sering kali di JS kami harus melakukan hal-hal secara asinkron, dalam hal ini, asinkron telah Anda bahas:

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

Fitur 'waktu' ini tidak terlalu berguna untuk sebagian besar kode aplikasi, tetapi seharusnya berguna untuk pengujian.

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

loop(something, 3)
Goro
sumber
0

Diberikan sebuah fungsi something:

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

Dan metode baru timesditambahkan ke Arrayprototipe:

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

Kode ini:

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

Keluaran ini:

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

Yang saya pikir menjawab pertanyaan Anda yang diperbarui (5 tahun kemudian) tetapi saya bertanya-tanya seberapa berguna memiliki pekerjaan ini pada array? Bukankah efeknya sama dengan memanggil [6].times(something), yang pada gilirannya dapat ditulis sebagai:

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

(meskipun penggunaan _sebagai variabel sampah mungkin akan mengganggu lodash atau garis bawah jika Anda menggunakannya)

pix
sumber
1
Menambahkan metode kustom ke objek JS asli dianggap sebagai praktik yang buruk.
Lior Elrom
Anda dapat menggunakan letsebagai for (let _ of Array(6)) something()untuk mencegah clobbering lodash di luar setidaknya.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
0

Array.from (ES6)

function doSomthing() {
    ...
}

Gunakan seperti ini:

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

Atau

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

Atau

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

Menggunakan Array.fromdan .forEach.

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

SeregPie
sumber
0

Dengan asumsi kita dapat menggunakan beberapa sintaks ES6 seperti operator penyebaran, kita akan ingin melakukan sesuatu sebanyak jumlah semua angka dalam koleksi.

Dalam hal ini, jika waktu sama dengan [1,2,3], jumlah total waktunya adalah 6, yaitu 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

Posting ini akan membantu jika logika di balik pembuatan dan penyebaran array tidak terlihat.

IliasT
sumber
0

Implementasi TypeScript:

Bagi Anda yang tertarik dengan cara mengimplementasikan String.timesdan Number.timesdengan cara yang termasuk tipe aman dan bekerja dengan thisArg, ini dia:

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

Tautan ke TypeScript Playground dengan beberapa contoh dapat ditemukan di sini

Posting ini mengimplementasikan solusi yang diposting oleh: Andreas Bergström , vinyll , Ozay Duman , & SeregPie

Noah Anderson
sumber