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


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)


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

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

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


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


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" :


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

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;

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


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


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

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

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

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

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.


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

loop(something, 3)

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)


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

0

Menggunakan Array.fromdan .forEach.

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


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.


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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.