làm <cái gì đó> N lần (cú pháp khai báo)


97

Có cách nào trong Javascript để viết một cái gì đó như thế này một cách dễ dàng không:

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

Bất kỳ thư viện nào có thể hỗ trợ một số cú pháp tương tự?

Cập nhật: để làm rõ - Tôi muốn something()được gọi lần lượt 1,2 và 3 lần cho mỗi lần lặp phần tử mảng


2
Tôi muốn nói rằng không có tính năng nào như thế này trong JS và đó là một trong 5 tính năng còn thiếu. Nó rất hữu ích để kiểm tra phần mềm, hơn bất cứ điều gì.
Alexander Mills

Câu trả lời:


48

Câu trả lời này dựa trên Array.forEach, không có bất kỳ thư viện nào, chỉ là vani bản địa .

Về cơ bản để gọi something()3 lần, hãy sử dụng:

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

xem xét chức năng sau:

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

Tiền đồn sẽ là

something
something
something

Để hoàn thành câu hỏi này, đây là cách để thực hiện something()lần lượt gọi 1, 2 và 3 lần:

Đó là năm 2017, bạn có thể sử dụng ES6:

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

hoặc trong ES5 cũ tốt:

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

Trong cả hai trường hợp, tiền đồn sẽ là

Tiền đồn sẽ là

something

something
something

something
something
something

(một lần, rồi hai lần, rồi 3 lần)


18
Điều này không chính xác vì nó không thỏa mãn phần này của câu hỏi: 'Tôi muốn một cái gì đó () được gọi là 1,2 và 3 lần'. Sử dụng mã somethingnày chỉ được gọi 3 lần, nên gọi là 6 lần.
Ian Newson,

Sau đó, tôi đoán nó đã được chọn là câu trả lời hay nhất vì nó có thể là một khởi đầu tốt hơn.
vinyll

3
Bạn cũng có thể sử dụng [...Array(i)]hoặc Array(i).fill(), tùy thuộc vào nhu cầu của bạn cho các chỉ mục thực tế.
Guido Bouman

Nếu bạn không quan tâm đến bất kỳ đối số trôi qua, sử dụng.forEach(something)
kvsm

88

Chỉ cần sử dụng một vòng lặp:

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

3
cảm ơn bạn! Tôi muốn được hưởng lợi từ một cú pháp khai báo (giống như Jasmine vv)
BreakPhreak

đúng, nhưng một chức năng cho vòng lặp cú pháp khai báo cũng sẽ tốt hơn
Alexander Mills

73

Có thể thay thế ES6.

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

Và, nếu bạn muốn nó "được gọi lần lượt 1,2 và 3 lần".

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

Cập nhật:

Lấy từ điền-mảng-với-không xác định

Đây có vẻ là cách tối ưu hóa hơn để tạo mảng ban đầu, tôi cũng đã cập nhật điều này để sử dụng chức năng bản đồ tham số thứ hai do @ felix-eve đề xuất.

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

3
Tôi nên cảnh báo điều này bằng cách nói rằng điều này là ổn nếu bạn chỉ đang viết nhanh một cái gì đó, nhưng hiệu suất quá khủng khiếp, vì vậy có lẽ không nên sử dụng nó cho đệ quy chuyên sâu hoặc trong sản xuất.
nverba

Nếu bạn đang sử dụng ES6, bạn có thể sử dụng map () thay vì forEach ()
Andy Ford

3
Nếu ngắn gọn là mục tiêu (và trên thực tế, ngay cả khi nó không phải), hãy chuyển hàm thay vì gọi nó:Array.from(Array(3)).forEach(something)
kvsm

1
Hoạt động với cả kết xuất biểu thức phản ứng.
Josh Sharkey

4
Array.from()có tham số thứ hai tùy chọn mapFn, cho phép bạn thực thi một hàm ánh xạ trên mỗi phần tử của mảng, do đó không cần sử dụng forEach. Bạn chỉ có thể làm:Array.from({length: 3}, () => somthing() )
Felix Eve

18

Vì bạn đề cập đến Dấu gạch dưới:

Giả sử flà hàm bạn muốn gọi:

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

sẽ thực hiện thủ thuật. Ví dụ: với f = function (x) { console.log(x); }, bạn sẽ nhận được trên bảng điều khiển của mình: 0 0 1 0 1 2


Thật vậy, tôi đã nghĩ rằng bạn muốn tách biệt.
ggozad

2
_(3).times(function(n){return n;});nên làm thủ thuật. Xem tài liệu tại đây.
Chip

18

Với lodash :

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

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

Hoặc nếu bạn muốn làm điều gì đó N lần :

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

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

Tham khảo liên kết này để lodashcài đặt


15

Tạo một Mảng và filltất cả các mục với phương thức undefinednhư vậy mapcó thể hoạt động:

Array.fill không có hỗ trợ IE

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

Nếu bạn muốn làm cho những điều trên trở nên "đơn giản" hơn, giải pháp hiện tại dựa trên quan điểm của tôi sẽ là:


Sử dụng vòng lặp cũ (ngược):

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

Hoặc dưới dạng khai báo "while" :


1
Đối với tâm trí, tôi đã chạy một chức năng uuid 50k lần để đảm bảo rằng nó không bao giờ sao chép một uuid. Vì vậy, tôi đã lập hồ sơ vòng lặp trên cùng so với vòng lặp dưới cùng chỉ cho các cú đá, chỉ chạy ở giữa tải trang bình thường bằng cách sử dụng các công cụ dành cho nhà phát triển chrome nếu không bị ngớ ngẩn, tôi nghĩ ~ 1,2 tỷ của nó so với việc sử dụng Array.indexOf () cộng với việc tạo ra 50k uuids. newschool = 1st-5561.2ms 2nd-5426.8ms | oldschool = 1st-4966.3ms / 2nd-4929.0ms Đạo đức của câu chuyện nếu bạn không ở trong tỷ + phạm vi, bạn sẽ không bao giờ nhận thấy sự khác biệt khi chạy 200, 1k, thậm chí 10k lần này để làm điều gì đó. Ai đó có thể tò mò như tôi.
rifi2k

Điều đó là chính xác và đã được biết đến trong nhiều năm. Các cách tiếp cận khác nhau không được trình bày vì lợi ích về tốc độ mà để hỗ trợ các trình duyệt cũ hơn.
vsync

3
Kỳ lạ là tất cả những người đọc chủ đề này, đều biết bạn đã không đưa ra các ví dụ để so sánh tốc độ của họ. Tôi chỉ tình cờ sử dụng chúng để chạy một thử nghiệm nhỏ và nghĩ rằng tôi sẽ chia sẻ một số thông tin mà ai đó trên đường có thể thấy thú vị. Tôi không thực sự chính xác bởi vì tôi đã không thực sự trả lời một câu hỏi chỉ hiển thị thông tin và đưa ra lời nhắc để không đổ mồ hôi với tốc độ của vòng lặp khi bạn chỉ làm một vài việc mà sẽ kết thúc trong vài mili giây. Nó cũng không thực sự được biết đến bởi vì thử nghiệm tương tự một năm trước, tức là có thể thấy một chậm hơn 50% vì các trình duyệt thay đổi liên tục.
rifi2k

10

Bạn cũng có thể làm điều tương tự với hủy cấu trúc như sau

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

hoặc nếu bạn cần chỉ mục

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

8

Nếu bạn không thể sử dụng Underscorejs, bạn có thể tự triển khai. Bằng cách đính kèm các phương thức mới vào nguyên mẫu Số và Chuỗi, bạn có thể làm như thế này (sử dụng các hàm mũi tên của 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) );

Bạn chỉ cần tạo một biểu thức hàm (với bất kỳ tên nào) và gán nó cho bất kỳ tên thuộc tính nào (trên nguyên mẫu) mà bạn muốn truy cập như:

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
Tôi sẽ phải điều tra lại xấu như thế nào đó là nó để vá các nguyên mẫu, nhưng thường nó chỉ tốt
Alexander Mills

2

Có một thư viện tuyệt vời tên là Ramda, tương tự như Underscore và Lodash, nhưng mạnh hơn.

const R = require('ramda');

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

Ramda chứa nhiều chức năng hữu ích. Xem tài liệu Ramda


Tôi thích thư viện này như một giải pháp FP hiện đại và thanh lịch.
momocow

1

Bạn có thể sử dụng độ dài của mảng để thực hiện số lần tác vụ của mình.

var arr = [1,2,3];

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

hoặc là

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

Bạn có thể gọi nó như thế này:

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

+1 - Điều này sử dụng khả năng JavaScript gốc để gọi các hàm với số lượng tham số thay đổi. Không cần thêm thư viện. Thoải mái giải pháp
RustyTheBoyRobot

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

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

hoặc (qua 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
  }
}

Sử dụng jQuery .each()

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

HOẶC LÀ

var x = [1,2,3];

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

BIÊN TẬP

Bạn có thể làm như bên dưới với JS thuần túy:

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

0

Chỉ cần sử dụng một vòng lặp lồng nhau (có thể bao gồm trong một hàm)

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

Sau đó, chỉ cần gọi nó như thế này:

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

0

Những câu trả lời này đều tốt và tốt và IMO @Andreas là tốt nhất, nhưng nhiều lần trong JS, chúng tôi phải thực hiện mọi thứ một cách không đồng bộ, trong trường hợp đó, không đồng bộ đã bao gồm:

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

Các tính năng 'thời gian' này không hữu ích cho hầu hết các mã ứng dụng, nhưng sẽ hữu ích cho việc thử nghiệm.


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

loop(something, 3)

0

Cho một hàm something:

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

Và một phương pháp mới timesđược thêm vào Arraynguyên mẫu:

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

Mã này:

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

Kết quả này:

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

Tôi nghĩ rằng câu trả lời cho câu hỏi cập nhật của bạn (5 năm sau) nhưng tôi tự hỏi nó hữu ích như thế nào khi công việc này trên một mảng? Hiệu ứng sẽ không giống như cách gọi [6].times(something), do đó có thể được viết là:

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

(mặc dù việc sử dụng _như một biến rác có thể sẽ làm tắc nghẽn hoặc gạch dưới nếu bạn đang sử dụng nó)


1
Việc thêm các phương thức tùy chỉnh vào một đối tượng JS gốc được coi là một thực tiễn không tốt.
Lior Elrom

Bạn có thể sử dụng letnhư trong for (let _ of Array(6)) something()để ngăn chặn tình trạng tắc nghẽn bên ngoài ít nhất.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

0

Array.from (ES6)

function doSomthing() {
    ...
}

Sử dụng nó như vậy:

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

Hoặc là

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

Hoặc là

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

0

Sử dụng Array.from.forEach.

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


0

Giả sử chúng ta có thể sử dụng một số cú pháp ES6 như toán tử spread, chúng ta sẽ muốn thực hiện điều gì đó nhiều lần với tổng của tất cả các số trong bộ sưu tập.

Trong trường hợp này nếu số lần bằng nhau [1,2,3], tổng số lần sẽ là 6, tức là 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

Bài đăng này sẽ hữu ích nếu logic đằng sau việc xây dựng và dàn trải một mảng không rõ ràng.


0

Triển khai TypeScript:

Đối với những người bạn quan tâm đến cách triển khai String.timesNumber.timestheo cách an toàn và hoạt động với thisArg, đây là:

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

Bạn có thể tìm thấy liên kết tới Sân chơi TypeScript với một số ví dụ tại đây

Bài đăng này thực hiện các giải pháp được đăng bởi: Andreas Bergström , vinyll , Ozay Duman , & SeregPie

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.