Идиома для "повторить п раз"?


РЕДАКТИРОВАТЬ: голоса, чтобы закрыть неправильные. Принятый ответ в " Повторите символ N раз" в целом не применим. Например:

>>> Array(3).map(Math.random)
[undefined, undefined, undefined]

В двух других ответах предлагается изменить встроенный класс, что я считаю совершенно неприемлемым.


Вот один несколько расточительный и непрактичный способ создать массив из 3 случайных чисел в JS:

>>> [1, 1, 1].map(Math.random)
[0.6324464592887568, 0.5969209806782131, 0.7362755801487572]

Использование фиктивного массива (например, [1, 1, 1]) просто чтобы можно было звонить map на это, есть, для достаточно большого п как расточительный (из памяти), так и нецелесообразный.

То, что хотелось бы, было бы что-то вроде гипотетического:

>>> repeat(3, Math.random)
[0.21425955396598173, 0.00226050232425945, 0.45261888146445495]

Что ближе всего может прийти к этому в "чистом JS"?

(Я знаю о Underscore, но в его API есть вещи, которые не имеют смысла для меня, такие как интерпретация mapтак что я пытаюсь этого избежать.)

7 ответов

Решение

Underscore.js имеет функцию times, которая делает именно то, что вы хотите:

_.times(3, Math.random)

Если вы не хотите использовать Underscore, вы можете просто написать свой собственный times функция (скопирована и слегка упрощена из исходного кода Underscore):

times = function(n, iterator) {
  var accum = Array(Math.max(0, n));
  for (var i = 0; i < n; i++) accum[i] = iterator.call();
  return accum;
};

Это можно сделать с помощью Array.prototype.map, но массив не может быть пустым. Заполните это сначала:

console.log(
    Array(3).fill().map(Math.random)
);

Объяснение:

new Array(3) Конструктор создает разреженный массив (или "дырявый" массив, как их называет команда V8) с тремя отверстиями в нем и длиной три. Это означает, что это эквивалентно [,,,], который создает [<empty>, <empty>, <empty>,] (обратите внимание на конечные запятые в JavaScript).

Array.prototype.map вызывается один раз для каждого элемента в массиве. Но поскольку пустой массив не имеет назначенных значений, обратный вызов вообще не вызывается. Например, [1,,2].map(v=>v*2) даст [2,,4]; средний слот пропущен.

Войти Array.prototype.fill(value, start?, end?): только с одним аргументом, он заполняет каждый слот в массиве указанным значением. Технически, первый параметр не является обязательным, но, опуская его, undefined используется в качестве значения. Это нормально, потому что значение все равно не используется. Сюда Array(3).fill() дает нам [undefined, undefined, undefined],

Теперь, когда в массиве есть значения, его можно отобразить, как показано выше.


Вы могли бы также spread пустой array в значения undefined перед отображением:

console.log(
    [...Array(3)].map(Math.random)
);

Объяснение:

Операторы массивов, представленные в ECMAScript2015 или более новые дыры угроз в массивах как undefined ценности. Array.prototype.map был введен в ES5 (IE, предшествовавший ES2015), где, как ни странно, дыры в массивах следует пропускать, создавая небольшую несогласованность в функциях JS Array в зависимости от того, в какой редакции ECMAScript они были выпущены.

Оператор спреда ... был введен в ES2015, поэтому, согласно спецификации, он преобразует любые дыры в данном массиве в значения undefined, Другими словами, [...Array(3)] дает нам [undefined, undefined, undefined], как Array(3).fill() делал выше.


Иногда вам может потребоваться посеять числа последовательно. Как отметил Kevin Danikowski, Array.prototype.map дает вам это из коробки, так как второй параметр является текущим ключом:

const Fibonacci = n => Math.round(((5**.5 + 1) / 2)**n / 5**.5);

console.log(
    Array(10).fill().map((_, i) => Fibonacci(++i))
);

Самый короткий элегантный ES6:

let times=(n,f)=>{while(n-->0)f();}

о, это не для создания массива, но все равно аккуратно!

times(3,()=>print('wow'))

или Рубиновый стиль:

Object.assign(Number.prototype,{times(f){x=this;while(x-->0)f();}})
3..times(()=>print('wow'))

Может быть Array.from обратный вызов может быть полезен:

var result = Array.from(Array(3), Math.random);

console.log(result);

Здесь есть небольшое преимущество перед использованием map: map уже нужен массив со всеми записями (может быть создан с fill или синтаксис распространения), чтобы затем создать окончательный массив из этого. Итого map Решение создаст n записей дважды. Array.from не нужен массив с записями, просто объект с length собственность будет делать, и Array(3) обеспечивает это.

Таким образом, в зависимости от ваших предпочтений, вышеперечисленное также может быть сделано следующим образом:

var result = Array.from({length:3}, Math.random);

console.log(result);

Наконец, если бы вы создали repeat Функция для этого, вы можете назвать аргумент length и используйте краткую запись ES6 для литералов объектов:

const repeat = (length, cb) => Array.from({length}, cb);

const result = repeat(3, Math.random);
console.log(result);

Современный способ создать это repeat функция:

repeat = (n, cb) => {[...Array(n)].forEach(cb)}

Затем вы можете использовать его с:

repeat(3, _ => console.log(Math.random()))

Будет вывод:

0.6324464592887568
0.5969209806782131
0.7362755801487572

Мне нравится этот способ:

[...Array(5).keys()].forEach(index =>
  console.log(`do something ${index}`
)

Вы можете сделать следующее:

      Iet res = 'n'.repeat(3).split('').map(Math.random)
Другие вопросы по тегам