зробити <щось> N разів (декларативний синтаксис)


96

Чи є спосіб у Javascript легко написати щось подібне:

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

Будь-яка бібліотека, яка може підтримувати подібний синтаксис, можливо?

Оновлення: для уточнення - я хотів би, something()щоб мене викликали 1,2 та 3 рази відповідно для кожної ітерації елемента масиву


2
Я б сказав, що такої функції в JS немає, і це 5 найкращих відсутніх функцій. Це дуже корисно для тестування програмного забезпечення, більше за все.
Олександр Міллс

Відповіді:


48

Ця відповідь заснована на Array.forEach, без жодної бібліотеки, просто рідній ванілі .

Щоб зателефонувати в основному something()3 рази, використовуйте:

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

враховуючи таку функцію:

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

Вихід буде

something
something
something

Щоб завершити ці запитання, ось спосіб зробити дзвінки something()1, 2 та 3 рази відповідно:

Настав 2017 рік, ви можете використовувати ES6:

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

або в старому доброму ES5:

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

В обох випадках вихід буде

Вихід буде

something

something
something

something
something
something

(раз, потім двічі, потім 3 рази)


18
Це неправильно, оскільки не задовольняє цю частину запитання: "Я хотів би, щоб щось () викликалося 1,2 та 3 рази". Використання цього коду somethingвикликається лише 3 рази, його слід викликати 6 разів.
Ян Ньюсон,

Тоді, я думаю, це було обрано як найкращу відповідь, оскільки це може бути гарним чистішим початком.
vinyll

3
Ви також можете використовувати [...Array(i)]або Array(i).fill(), залежно від ваших потреб у фактичних індексах.
Guido Bouman

Якщо вас не цікавлять передані аргументи, використовуйте.forEach(something)
kvsm

88

Просто використовуйте цикл:

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

3
Дякую! Я хотів би скористатися декларативним синтаксисом (так само, як Жасмин тощо)
BreakPhreak

вірно, але функціональний декларативний синтаксис for-loop також був би набагато кращим
Alexander Mills

71

Можлива альтернатива ES6.

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

І, якщо ви хочете, щоб його "викликали 1,2 і 3 рази відповідно".

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

Оновлення:

Взято з fill-arrays-with-undefined

Здається, це більш оптимізований спосіб створення початкового масиву, я також оновив його, щоб використовувати другу функцію відображення параметрів, запропоновану @ felix-eve.

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

3
Я мав би застерегти це, сказавши, що це нормально, якщо ви просто швидко створюєте сценарії, але продуктивність жахлива, тому не використовуйте її для інтенсивної рекурсії або взагалі, можливо, у виробництві.
nverba

Якщо ви йдете на ES6, ви можете використовувати map () замість forEach ()
Енді Форд,

3
Якщо стислість є метою (а насправді, навіть якщо це не так), передайте функцію замість того, щоб викликати її:Array.from(Array(3)).forEach(something)
kvsm

1
Також працює з наданням виразу реакції.
Джош Шаркі

4
Array.from()має необов’язковий другий параметр mapFn, який дозволяє виконувати функцію відображення на кожному елементі масиву, тому немає необхідності використовувати forEach. Ви можете просто зробити:Array.from({length: 3}, () => somthing() )
Фелікс Єва

19

Оскільки ви згадали про підкреслення:

Припустимо, fце функція, яку ви хочете викликати:

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

зробить трюк. Наприклад, за допомогою f = function (x) { console.log(x); }, ви отримаєте на своїй консолі: 0 0 1 0 1 2


Справді, я думав, що ти хочеш розлуки.
ggozad

2
_(3).times(function(n){return n;});повинен зробити трюк. Документи дивіться тут.
Чіп

18

З лодашем :

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

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

Або якщо ви хочете зробити щось N разів :

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

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

Зверніться до цього посилання для lodashвстановлення


14

Створіть масив, і fillвсі елементи з undefinedтаким mapметодом можуть працювати:

Array.fill не підтримує IE

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

Якщо ви хочете зробити вищезазначене більш "деклеративним", моє рішення, засноване на думках, буде таким:


Використання олдскульного (зворотного) циклу:

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

Або як декларативне "while" :


1
Для розуму я запустив функцію uuid 50 тис. Разів, щоб переконатися, що вона ніколи не дублювала uuid. Тож я профілював верхній цикл проти нижнього лише для ударів, просто працюючи в середині звичайного завантаження сторінки, використовуючи інструменти chrome dev, якщо я не тупий, я думаю, його ~ 1,2 мільярда порівнюється за допомогою Array.indexOf () плюс генерує 50 тис. Користувацьких ресурсів. школа новин = 1-а 5561,2 мс 2-а-5426,8 мс | oldschool = 1st-4966.3ms / 2nd-4929.0ms Мораль історії, якщо u не перебуває в діапазоні мільярдів +, ви ніколи не помітите різниці, коли б ці 200, 1k, навіть 10k разів щось зробили. Припустив, хтось може бути цікавим, як я.
rifi2k

Це правильно і відомо вже багато років. Різні підходи були представлені не для переваг швидкості, а для підтримки старих браузерів.
vsync

3
Очевидно, кожен, хто читає цю тему, знає, що ви не подавали приклади, щоб порівняти їх швидкість. Я просто скористався ними, щоб провести невеликий тест, і зрозумів, що поділюсь деякою інформацією, яка комусь у дорозі може здатися цікавою. Я насправді не правильний, тому що я насправді не відповідав на запитання, а лише відображав інформацію та давав нагадування, щоб не потіти швидкість циклу, коли ви робите лише кілька речей, які в будь-якому випадку закінчаться через пару мс. Це насправді також невідомо, оскільки той самий тест рік тому, тобто може виявитись на 50% повільнішим, оскільки браузери постійно змінюються.
rifi2k

10

Ви також можете зробити те ж саме з деструктуризацією наступним чином

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

або якщо вам потрібен індекс

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

8

Якщо ви не можете використовувати Underscorejs, ви можете реалізувати це самостійно. Приєднавши нові методи до прототипів Number і String, ви можете зробити це так (використовуючи функції стрілок 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) );

Вам просто потрібно створити вираз функції (з будь-якою назвою) і призначити його будь-якому імені властивості (на прототипах), до якого ви хотіли б отримати доступ як:

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
Мені довелося б повторно дослідити, наскільки погано латати прототип, але зазвичай це просто чудово
Олександр Міллс

2

Існує фантастична бібліотека Ramda, яка схожа на Underscore та Lodash, але є потужнішою.

const R = require('ramda');

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

Рамда містить безліч корисних функцій. Див. Документацію Ramda


Я люблю цю бібліотеку як сучасне та елегантне рішення FP.
momocow

1

Ви можете використовувати довжину масиву, щоб виконати кількість разів вашого завдання.

var arr = [1,2,3];

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

або

 var arr = [1,2,3];

 do
 {


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

1

ви можете використовувати

Array.forEach

приклад:

function logArrayElements(element, index, array) {  
    console.log("a[" + index + "] = " + element);  
}  
[2, 5, 9].forEach(logArrayElements)

або за допомогою jQuery

$.each([52, 97], function(index, value) { 
  alert(index + ': ' + value); 
});

http://api.jquery.com/jQuery.each/


Схоже forEach, підтримується в IE лише з версії 9: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
Бруно,

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

Ви можете назвати це так:

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

+1 - Це використовує власну можливість JavaScript викликати функції із змінною кількістю параметрів. Не потрібна додаткова бібліотека. Приємне рішення
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(); );

або (через 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
  }
}

Використання jQuery .each()

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

АБО

var x = [1,2,3];

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

РЕДАГУВАТИ

Ви можете зробити, як показано нижче, із чистою JS:

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

0

Просто використовуйте вкладений цикл (можливо, укладений у функцію)

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

Тоді просто назвіть це так:

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

0

Ці відповіді хороші та якісні, а IMO @Andreas - найкращий, але багато разів у JS нам доводиться робити речі асинхронно, в такому випадку асинхронізація охоплювала:

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

Ці функції "часу" не дуже корисні для більшості програмних кодів, але мають бути корисними для тестування.


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

loop(something, 3)

0

Дана функція something:

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

І новий метод, timesдоданий до Arrayпрототипу:

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

Цей код:

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

Виводить це:

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

Я думаю, що я відповідаю на ваше оновлене запитання (через 5 років), але мені цікаво, наскільки корисно мати цю роботу над масивом? Чи не буде ефект таким самим, як дзвінок [6].times(something), який, у свою чергу, може бути записаний як:

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

(хоча використання _як сміттєвої змінної, ймовірно, призведе до зменшення чи підкреслення, якщо ви використовуєте її)


1
Вважається поганою практикою додавати власні методи до власного об’єкта JS.
Ліор Елром,

Ви можете використовувати letяк в, for (let _ of Array(6)) something()щоб запобігти загрожуванню лошади як мінімум щонайменше.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

0

Array.from (ES6)

function doSomthing() {
    ...
}

Використовуйте його так:

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

Або

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

Або

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


0

Припускаючи, що ми можемо використовувати якийсь синтаксис ES6, як оператор поширення, ми хочемо зробити щось стільки разів, скільки буде сума всіх чисел у колекції.

У цьому випадку, якщо times дорівнює [1,2,3], загальна кількість разів буде 6, тобто 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

Цей пост повинен бути корисним, якщо логіка побудови та розповсюдження масиву не очевидна.


0

Впровадження TypeScript:

Для тих з вас, хто цікавиться, як це реалізувати String.timesта Number.timesбезпечно для типу та працює з цим thisArg, ось:

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 з деякими прикладами тут

Цей пост реалізує рішення, опубліковані: Andreas Bergström , vinyll , Ozay Duman , & SeregPie

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.