Есть ли в JavaScript метод типа range () для генерации диапазона в заданных пределах?


876

В PHP вы можете сделать ...

range(1, 3); // Array(1, 2, 3)
range("A", "C"); // Array("A", "B", "C")

То есть есть функция, которая позволяет вам получать диапазон чисел или символов, передавая верхнюю и нижнюю границы.

Есть ли что-нибудь встроенное в JavaScript для этого? Если нет, как бы я это реализовал?


1
В Prototype.js есть $Rфункция, но, на самом деле, я так не думаю.
И Цзян

На этот (связанный) вопрос есть несколько отличных ответов: stackoverflow.com/questions/6299500/…
btk

Array.from("ABC") //['A', 'B', 'C']Это самая близкая вещь, которую я могу найти для второй части вопроса.
Andrew_1510

@ Andrew_1510 Вы также можете использовать split("")там
alex

1
Когда привязанный любовник равен нулю, этот oneliner:Array.apply(null, { length: 10 }).map(eval.call, Number)
csharpfolk

Ответы:


1505

чисел

[...Array(5).keys()];
 => [0, 1, 2, 3, 4]

Итерация персонажа

String.fromCharCode(...[...Array('D'.charCodeAt(0) - 'A'.charCodeAt(0) + 1).keys()].map(i => i + 'A'.charCodeAt(0)));
 => "ABCD"

итерация

for (const x of Array(5).keys()) {
  console.log(x, String.fromCharCode('A'.charCodeAt(0) + x));
}
 => 0,"A" 1,"B" 2,"C" 3,"D" 4,"E"

Как функции

function range(size, startAt = 0) {
    return [...Array(size).keys()].map(i => i + startAt);
}

function characterRange(startChar, endChar) {
    return String.fromCharCode(...range(endChar.charCodeAt(0) -
            startChar.charCodeAt(0), startChar.charCodeAt(0)))
}

Как типизированные функции

function range(size:number, startAt:number = 0):ReadonlyArray<number> {
    return [...Array(size).keys()].map(i => i + startAt);
}

function characterRange(startChar:string, endChar:string):ReadonlyArray<string> {
    return String.fromCharCode(...range(endChar.charCodeAt(0) -
            startChar.charCodeAt(0), startChar.charCodeAt(0)))
}

_.range()функция lodash.js

_.range(10);
 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
 => [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
 => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
String.fromCharCode(..._.range('A'.charCodeAt(0), 'D'.charCodeAt(0) + 1));
 => "ABCD"

Старые не es6 браузеры без библиотеки:

Array.apply(null, Array(5)).map(function (_, i) {return i;});
 => [0, 1, 2, 3, 4]

console.log([...Array(5).keys()]);

(Кредит ES6 Нилсу Петерсону и другим комментаторам)


72
Потому что, если это полезно где-то, это, вероятно, полезно в JS. (JS может делать вещи типа функционального программирования, которые могут извлечь выгоду из диапазона (0 операторов. Это и тысяча других причин могут быть полезны в некоторых полуторных случаях)
Lodewijk

5
Есть идеи, почему простое использование (new Array(5)).map(function (value, index) { return index; })не сработает? Это возвращается [undefined × 5]для меня в Chrome DevTools.
Льюис

12
@Lewis Потому что у массива, определенного с этим, есть пустые слоты, которые не будут повторяться ни с map()одним из его друзей.
Алекс

65
Array.from (Array (5) .keys ())
нильс петерсон

17
Array(5).fill()также сопоставимо
Нильс Петерсон

333

Для чисел вы можете использовать ES6 Array.from(), который работает во все эти дни, кроме IE:

Укороченная версия:

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

Более длинная версия:

Array.from(new Array(20), (x,i) => i)

который создает массив от 0 до 19 включительно. Это может быть сокращено до одной из следующих форм:

Array.from(Array(20).keys())
// or
[...Array(20).keys()]

Также можно указать нижнюю и верхнюю границы, например:

Array.from(new Array(20), (x,i) => i + *lowerBound*)

Статья, описывающая это более подробно: http://www.2ality.com/2014/05/es6-array-methods.html


50
Первый пример можно даже упростить до [... Array (20) .keys ()]
Делапуит

27
Чуть более кратким, чем Array.from()метод, и быстрее, чем оба:Array(20).fill().map((_, i) => i)
Стю Кокс

2
@ Delapouite Отлично! Вы должны сделать это отдельным ответом, и я проголосую за это! Это также идеальный ответ на этот дубликат .
Джиб

9
@Delapouite @jib И это тоже:Array.from({length: end - start}, (v, k) => k + start)
Адитья Сингх

1
@ icc97 Да, линтеры могут жаловаться, хотя в JavaScript пропускается аргумент функции, который определен так же, как и передача undefined, поэтому fill()(без аргумента) сам по себе не ошибается . Значение заполнения не используется в этом решении, поэтому, если хотите, вы можете использовать, fill(0)чтобы сохранить несколько символов.
Стю Кокс

122

Моя новая любимая форма ( ES2015 )

Array(10).fill(1).map((x, y) => x + y)

И если вам нужна функция с stepпараметром:

const range = (start, stop, step = 1) =>
  Array(Math.ceil((stop - start) / step)).fill(start).map((x, y) => x + y * step)

5
let range = (начало, остановка, шаг = 1) => Массив (остановка - старт) .fill (запуск) .map ((x, y) => x + y * step)
родится

4
@rodfersou К вашему сведению: ваш пример неверен. stopна самом деле это не стоп / конечная позиция, а счет / расстояние. (без обид, просто чтобы люди знали об
опечатке

4
Для запутанных - из-за правки Родферсу после комментария Ф. Лекша, его код теперь правильный.
Eedrah

1
Аргумент, который вы передаете Array(Math.ceil((stop - start) / step) + 1), нуждается +1в конце, чтобы действительно имитировать "включающее" поведение php.
Йохан Деттмар

3
Это лучший ответ, который фактически отвечает на полный вопрос о функции Javascript, которая полностью реализует rangeметод. Все остальные над этим (кроме lodash _.range) реализуют базовые итераторы, а не фактическую функцию range с start, stop и step
icc97

99

Вот мои 2 цента:

function range(start, count) {
  return Array.apply(0, Array(count))
    .map((element, index) => index + start);
}

1
Отличное использование функций высокого порядка.
Farzad YZ

5
Это на самом деле неправильно, потому что вопрос задает начальные и конечные значения. Не начинать и считать / расстояние.
Джеймс Роби,

73

Это работает для символов и чисел, идущих вперед или назад с дополнительным шагом.

var range = function(start, end, step) {
    var range = [];
    var typeofStart = typeof start;
    var typeofEnd = typeof end;

    if (step === 0) {
        throw TypeError("Step cannot be zero.");
    }

    if (typeofStart == "undefined" || typeofEnd == "undefined") {
        throw TypeError("Must pass start and end arguments.");
    } else if (typeofStart != typeofEnd) {
        throw TypeError("Start and end arguments must be of same type.");
    }

    typeof step == "undefined" && (step = 1);

    if (end < start) {
        step = -step;
    }

    if (typeofStart == "number") {

        while (step > 0 ? end >= start : end <= start) {
            range.push(start);
            start += step;
        }

    } else if (typeofStart == "string") {

        if (start.length != 1 || end.length != 1) {
            throw TypeError("Only strings with one character are supported.");
        }

        start = start.charCodeAt(0);
        end = end.charCodeAt(0);

        while (step > 0 ? end >= start : end <= start) {
            range.push(String.fromCharCode(start));
            start += step;
        }

    } else {
        throw TypeError("Only string and number types are supported");
    }

    return range;

}

jsFiddle .

Если вам подходит расширение родных типов, присвойте его Array.range.


53

Простая функция диапазона:

function range(start, stop, step) {
    var a = [start], b = start;
    while (b < stop) {
        a.push(b += step || 1);
    }
    return a;
}

Для включения типа данных BitInt может быть включена некоторая проверка, гарантирующая, что все переменные одинаковы typeof start:

function range(start, stop, step) {
    var a = [start], b = start;
    if (typeof start == 'bigint') {
        stop = BigInt(stop)
        step = step? BigInt(step): 1n;
    } else
        step = step || 1;
    while (b < stop) {
        a.push(b += step);
    }
    return a;
}

Чтобы удалить значения выше, чем определено, stopнапример, range(0,5,2)будет включать 6, что не должно быть.

function range(start, stop, step) {
    var a = [start], b = start;
    while (b < stop) {
        a.push(b += step || 1);
    }
    return (b > stop) ? a.slice(0,-1) : a;
}

3
PLUS UNO для удобного и читабельного использования. Лучший фрагмент кода, который я видел за долгое время.
Монсто

1
Это не работает , когда step != 1, то whileусловие необходимо принимать stepво внимание. Моя обновленная версия со stepзначением по умолчанию : диапазон функций (начало, остановка, шаг) {шаг = шаг || 1 var a = [start], b = start; while ((b + step) <stop) {console.log ("b:" + b + ". a:" + a + "."); Ь + = шаг; a.push (б); } return a; }
daveharris

@daveharris Я добавил шаг по умолчанию выше (step || 1).
Мистер Поливирл,

36
Array.range= function(a, b, step){
    var A= [];
    if(typeof a== 'number'){
        A[0]= a;
        step= step || 1;
        while(a+step<= b){
            A[A.length]= a+= step;
        }
    }
    else{
        var s= 'abcdefghijklmnopqrstuvwxyz';
        if(a=== a.toUpperCase()){
            b=b.toUpperCase();
            s= s.toUpperCase();
        }
        s= s.substring(s.indexOf(a), s.indexOf(b)+ 1);
        A= s.split('');        
    }
    return A;
}


    Array.range(0,10);
    // [0,1,2,3,4,5,6,7,8,9,10]

    Array.range(-100,100,20);
    // [-100,-80,-60,-40,-20,0,20,40,60,80,100]

    Array.range('A','F');
    // ['A','B','C','D','E','F')

    Array.range('m','r');
    // ['m','n','o','p','q','r']

Вы действительно не должны проверять методы на Arrayпрототип.
connectyourcharger

Этот метод работает только с целыми числами и символами. Если параметры имеют значение null, undefined, NaN, логическое значение, массив, объект и т. Д., Этот метод возвращает следующую ошибку undefined method toUpperCase to etc:!
Виктор

`` `if (typeof from! == 'number' && typeof from! == 'string') {throw new TypeError ('Первый параметр должен быть числом или символом')} if (typeof to! == ' number '&& typeof to! ==' string ') {throw new TypeError (' Первый параметр должен быть числом или символом ')} `` `
Victor

36

Хорошо, в JavaScript у нас нет такой range()функции, как PHP , поэтому нам нужно создать эту функцию, что довольно просто, я пишу пару однострочных функций для вас и разделяю их для чисел и алфавитов, как показано ниже:

для номеров :

function numberRange (start, end) {
  return new Array(end - start).fill().map((d, i) => i + start);
}

и назовите это как:

numberRange(5, 10); //[5, 6, 7, 8, 9]

для алфавитов :

function alphabetRange (start, end) {
  return new Array(end.charCodeAt(0) - start.charCodeAt(0)).fill().map((d, i) => String.fromCharCode(i + start.charCodeAt(0)));
}

и назовите это как:

alphabetRange('c', 'h'); //["c", "d", "e", "f", "g"]

2
Я думаю, что есть ошибки в этих функциях. Должно быть Array(end - start + 1)и Array(end.charCodeAt(0) - start.charCodeAt(0) + 1).
earcanal

24

Удобная функция, чтобы сделать трюк, запустите фрагмент кода ниже

function range(start, end, step, offset) {
  
  var len = (Math.abs(end - start) + ((offset || 0) * 2)) / (step || 1) + 1;
  var direction = start < end ? 1 : -1;
  var startingPoint = start - (direction * (offset || 0));
  var stepSize = direction * (step || 1);
  
  return Array(len).fill(0).map(function(_, index) {
    return startingPoint + (stepSize * index);
  });
  
}

console.log('range(1, 5)=> ' + range(1, 5));
console.log('range(5, 1)=> ' + range(5, 1));
console.log('range(5, 5)=> ' + range(5, 5));
console.log('range(-5, 5)=> ' + range(-5, 5));
console.log('range(-10, 5, 5)=> ' + range(-10, 5, 5));
console.log('range(1, 5, 1, 2)=> ' + range(1, 5, 1, 2));

вот как это использовать

диапазон (начало, конец, шаг = 1, смещение = 0);

  • включительно - вперед range(5,10) // [5, 6, 7, 8, 9, 10]
  • включительно - назад range(10,5) // [10, 9, 8, 7, 6, 5]
  • шаг назад range(10,2,2) // [10, 8, 6, 4, 2]
  • эксклюзив - форвард range(5,10,0,-1) // [6, 7, 8, 9] not 5,10 themselves
  • смещение - развернуть range(5,10,0,1) // [4, 5, 6, 7, 8, 9, 10, 11]
  • смещение - сжатие range(5,10,0,-2) // [7, 8]
  • шаг - расширить range(10,0,2,2) // [12, 10, 8, 6, 4, 2, 0, -2]

Надеюсь, что вы найдете ее полезной.


И вот как это работает.

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

  • (step || 1)=> И другие, как это означает использовать значение, stepи если оно не было предоставлено использовать1 вместо
  • Начнем с вычисления длины массива результатов, используя (Math.abs(end - start) + ((offset || 0) * 2)) / (step || 1) + 1) чтобы упростить его (разница * смещение в обоих направлениях / шагах)
  • После получения длины мы создаем пустой массив с инициализированными значениями, используя new Array(length).fill(0); здесь проверку
  • Теперь у нас есть массив [0,0,0,..]той длины, которую мы хотим. Мы отображаем его и возвращаем новый массив со значениями, которые нам нужны, используяArray.map(function() {})
  • var direction = start < end ? 1 : 0;Очевидно, если startне меньше, чемend мы должны двигаться назад. Я имею в виду переход от 0 до 5 или наоборот
  • startingPoint+ stepSize* На каждой итерации indexдает нам значение, которое нам нужно

8
Удобно, конечно. Просто? Позволю себе не согласиться; независимо от того, что вы делаете это одним вкладышем. Исходя из Python это шок.
PascalVKooten

@PascalvKooten, да, конечно, было бы замечательно, если бы был встроенный метод для этого, как, например, python, но это был самый простой способ, которым я мог воспользоваться. И это оказалось полезным в моих проектах.
Азерафати

Опубликовать такой сложный фрагмент кода, особенно в виде одной строки, без объяснения того, как он работает? Плохой пример хорошего SO ответа, независимо от того, работает он или нет.
Madbreaks

1
@ Ошибки, да, ты прав. Я был наивен, чтобы сделать это одним лайнером. просто хотел дать всем быстрое и простое решение
azerafati

22
var range = (l,r) => new Array(r - l).fill().map((_,k) => k + l);

@nikkwong, _это просто имя аргумента в обратном вызове сопоставления. Знаете, в некоторых языках вы бы использовали имя _в качестве имени, чтобы указать, что переменная не используется.
Клесун

Здесь, однако, _не передаются аргументы для range. Почему бы нет?
Никк Вонг

2
Очень аккуратный! Хотя важно отметить, что он не работает ни на одном IE или Opera.
Рафаэль Ксавье

4
Этот ответ нуждается в объяснении, так как он плохо подходит для SO.
Madbreaks

@RafaelXavier будет работать в IE с полифилом Array.fill ()
mwag

18

Использование оператора спреда Harmony и функций стрелок:

var range = (start, end) => [...Array(end - start + 1)].map((_, i) => start + i);

Пример:

range(10, 15);
[ 10, 11, 12, 13, 14, 15 ]

это лучший ответ!
Генри Х.

1
Не самый быстрый, хотя.
Mjwrazor

Что символ подчеркивания '_' представляет в этом случае?
Олег

@OlehBerehovskyi Это параметр лямбда-функции, который вы фактически не собираетесь использовать. Линтер, который предупреждает о неиспользуемых переменных, должен игнорировать его.
Мика Золту

18

--- ОБНОВЛЕНИЕ (Спасибо @lokhmakov за упрощение) ---

Другая версия, использующая генераторы ES6 (см. Отличный ответ Паоло Моретти с генераторами ES6 ):

const RANGE = (x,y) => Array.from((function*(){
  while (x <= y) yield x++;
})());

console.log(RANGE(3,7));  // [ 3, 4, 5, 6, 7 ]

Или, если нам нужна только итерация, то:

const RANGE_ITER = (x,y) => (function*(){
  while (x <= y) yield x++;
})();

for (let n of RANGE_ITER(3,7)){
  console.log(n);
}

// 3
// 4
// 5
// 6
// 7

--- ОРИГИНАЛЬНЫЙ код был: ---

const RANGE = (a,b) => Array.from((function*(x,y){
  while (x <= y) yield x++;
})(a,b));

а также

const RANGE_ITER = (a,b) => (function*(x,y){
  while (x <= y) yield x++;
})(a,b);

1
Просто const range = (x, y) => Array.from(function* () { while (x <= y) yield x++; }())
лохмаков

@lokhmakov Да, вы правы. благодарю вас! Просто применил свой код в моем ответе.
Герой Цюй

15

Сделал некоторые исследования некоторых различных функций диапазона. Посмотрите в jsperf сравнение различных способов выполнения этих функций. Конечно, не идеальный или исчерпывающий список, но должен помочь :)

Победитель...

function range(lowEnd,highEnd){
    var arr = [],
    c = highEnd - lowEnd + 1;
    while ( c-- ) {
        arr[c] = highEnd--
    }
    return arr;
}
range(0,31);

Технически это не самая быстрая на Firefox, но сумасшедшая разница в скорости (imho) на Chrome компенсирует это.

Также интересно наблюдать, насколько Chrome работает с этими функциями массива быстрее, чем Firefox. Хром как минимум в 4 или 5 раз быстрее .


Обратите внимание, что это сравнивалось с функциями диапазона, которые включали параметр размера шага
binaryfunt

15

Стандартный Javascript не имеет встроенной функции для генерации диапазонов. Некоторые фреймворки javascript добавляют поддержку таких функций, или, как уже отмечали другие, вы всегда можете свернуть свои собственные.

Если вы хотите перепроверить, основным ресурсом является стандарт ECMA-262 .


Хотя я уверен, что в 2010 году это был отличный ответ, его уже нельзя считать лучшим подходом. Вы не должны расширять встроенные типы, как это делал Prototype.js D
Дана Вудман

@DanaWoodman спасибо за то, что подняли этот вопрос - я обновил ответ, чтобы убрать ссылку на Prototype.js, поскольку он действительно устарел в 2018 году
Майк Динеску,

22
Ну, это совсем не помогло.
Pithikos

@Pithikos Я вижу, что этот вопрос был отредактирован, так как он был задан изначально, и ОП хотел узнать, есть ли в JS встроенная функция диапазона.
Майк Динеску

13

Вы можете использовать lodash или Undescore.js range :

var range = require('lodash/range')
range(10)
// -> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

В качестве альтернативы, если вам нужен только последовательный диапазон целых чисел, вы можете сделать что-то вроде:

Array.apply(undefined, { length: 10 }).map(Number.call, Number)
// -> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

В ES6 rangeмогут быть реализованы генераторы :

function* range(start=0, end=null, step=1) {
  if (end == null) {
    end = start;
    start = 0;
  }

  for (let i=start; i < end; i+=step) {
    yield i;
  }
}

Эта реализация экономит память при итерации больших последовательностей, поскольку ей не нужно материализовать все значения в массиве:

for (let i of range(1, oneZillion)) {
  console.log(i);
}

Часть ES6 теперь является правильным ответом на этот вопрос. Я бы порекомендовал удалить другие части, которые покрыты другими ответами.
Евровидение

генераторы несколько странны, если их использовать вне цикла: x = range (1, 10); // {} x; // {} // выглядит как пустая карта WTF!?! x.next (). value; // OK 1; x [3] // не определено, только с реальным массивом
Anona112

@ Anona112 вы можете использовать Array.fromдля преобразования генераторов в экземпляры массивов и проверки выходных данных.
Паоло Моретти

10

Интересной задачей было бы написать самую короткую функцию для этого. Рекурс на помощь!

function r(a,b){return a>b?[]:[a].concat(r(++a,b))}

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

Дополнительным бонусом является то, что это запутывает. Потому что мы все знаем, как важно скрыть наш код от посторонних глаз.

Чтобы по-настоящему и полностью запутать функцию, сделайте это:

function r(a,b){return (a<b?[a,b].concat(r(++a,--b)):a>b?[]:[a]).sort(function(a,b){return a-b})}

4
Коротко! = Просто, но проще, тем лучше. Вот более легкая для чтения версия: const range = (a, b) => (a>=b) ? [] : [a, ...range(a+1, b)]с использованием синтаксиса
ES6

1
@nafg: const range = (a, b, Δ = 1) => (a > b) ? [] : [a, ...range(a + Δ, b, Δ)];. Также выслушав весь ответ за комментарий.
7vujy0f0hy

10

Это может быть не лучшим способом. Но если вы хотите получить диапазон чисел в одной строке кода. Например 10 - 50

Array(40).fill(undefined).map((n, i) => i + 10)

Где 40 (конец - начало) и 10 - начало. Это должно вернуть [10, 11, ..., 50]


9

Я хотел бы написать что-то вроде этого:

function range(start, end) {
    return Array(end-start).join(0).split(0).map(function(val, id) {return id+start});
}  

range(-4,2);
// [-4,-3,-2,-1,0,1]

range(3,9);
// [3,4,5,6,7,8]

Он ведет себя аналогично диапазону Python:

>>> range(-4,2)
[-4, -3, -2, -1, 0, 1]

8

Довольно минималистичная реализация, в которой интенсивно используется ES6, может быть создана следующим образом, обратив особое внимание на Array.from()статический метод:

const getRange = (start, stop) => Array.from(
  new Array((stop - start) + 1),
  (_, i) => i + start
);

Как примечание, я создал Gist, в котором я сделал getRange()своего рода «расширенную» функцию. В частности, я стремился охватить крайние случаи, которые могут быть рассмотрены без изменений в приведенном выше варианте. Кроме того, я добавил поддержку буквенно-цифровых диапазонов. Другими словами, вызов его с помощью двух предоставленных входных данных, таких как 'C'и 'K'(в этом порядке), возвращает массив, значения которого представляют собой последовательный набор символов от буквы «C» (включительно) до буквы «K» ( getRange('C', 'K'); // => ["C", "D", "E", "F", "G", "H", "I", "J"]
исключая

вам не нужно newключевое слово
Soldeplata Saketos

8

range(start,end,step): С итераторами ES6

Вы запрашиваете только верхнюю и нижнюю границы. Здесь мы создаем один шаг тоже.

Вы можете легко создать range()функцию генератора, которая может функционировать как итератор. Это означает, что вам не нужно предварительно генерировать весь массив.

function * range ( start, end, step = 1 ) {
  let state = start;
  while ( state < end ) {
    yield state;
    state += step;
  }
  return;
};

Теперь вы можете захотеть создать что-то, что предварительно сгенерирует массив из итератора и вернет список. Это полезно для функций, которые принимают массив. Для этого мы можем использоватьArray.from()

const generate_array = (start,end,step) =>
  Array.from( range(start,end,step) );

Теперь вы можете легко создать статический массив,

const array1 = generate_array(1,10,2);
const array1 = generate_array(1,7);

Но когда что-то требует итератор (или дает вам возможность использовать итератор), вы также можете легко его создать.

for ( const i of range(1, Number.MAX_SAFE_INTEGER, 7) ) {
  console.log(i)
}

Особые заметки


7

Хотя это не из PHP , но имитация rangeиз Python .

function range(start, end) {
    var total = [];

    if (!end) {
        end = start;
        start = 0;
    }

    for (var i = start; i < end; i += 1) {
        total.push(i);
    }

    return total;
}

console.log(range(10)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
console.log(range(0, 10)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(range(5, 10)); // [5, 6, 7, 8, 9] 

+1 за самый быстрый. с массивом -36768 - 36768, занял 3 мс, 2-е место было 13 мс и имеет IDE красные линии.
mjwrazor

7

Что касается генерации числового массива для заданного диапазона, я использую это:

function range(start, stop)
{
    var array = [];

    var length = stop - start; 

    for (var i = 0; i <= length; i++) { 
        array[i] = start;
        start++;
    }

    return array;
}

console.log(range(1, 7));  // [1,2,3,4,5,6,7]
console.log(range(5, 10)); // [5,6,7,8,9,10]
console.log(range(-2, 3)); // [-2,-1,0,1,2,3]

Очевидно, это не будет работать для алфавитных массивов.


Установка array = []внутри цикла может не дать вам то, что вы хотите.
Алекс

@alex, спасибо. Вы правы, я также забыл увеличивать параметр "start" на каждом проходе цикла. Это сейчас исправлено.
Джаскелл

Он все равно не даст желаемого результата, если я хочу диапазон 5-10, он даст мне [5, 6, 7, 8, 9, 10, 11, 12, 13, 14], я бы ожидал только первую половину этого массива.
Алекс

@alex, еще раз спасибо, я не учел ограничение длины на основе входных данных. Смотрите обновленную версию.
Джаскелл

6

Использование генераторов Harmony , поддерживаемых всеми браузерами, кроме IE11 :

var take = function (amount, generator) {
    var a = [];

    try {
        while (amount) {
            a.push(generator.next());
            amount -= 1;
        }
    } catch (e) {}

    return a;
};

var takeAll = function (gen) {
    var a = [],
        x;

    try {
        do {
            x = a.push(gen.next());
        } while (x);
    } catch (e) {}

    return a;
};

var range = (function (d) {
    var unlimited = (typeof d.to === "undefined");

    if (typeof d.from === "undefined") {
        d.from = 0;
    }

    if (typeof d.step === "undefined") {
        if (unlimited) {
            d.step = 1;
        }
    } else {
        if (typeof d.from !== "string") {
            if (d.from < d.to) {
                d.step = 1;
            } else {
                d.step = -1;
            }
        } else {
            if (d.from.charCodeAt(0) < d.to.charCodeAt(0)) {
                d.step = 1;
            } else {
                d.step = -1;
            }
        }
    }

    if (typeof d.from === "string") {
        for (let i = d.from.charCodeAt(0); (d.step > 0) ? (unlimited ? true : i <= d.to.charCodeAt(0)) : (i >= d.to.charCodeAt(0)); i += d.step) {
            yield String.fromCharCode(i);
        }
    } else {
        for (let i = d.from; (d.step > 0) ? (unlimited ? true : i <= d.to) : (i >= d.to); i += d.step) {
            yield i;
        }
    }
});

Примеры

принимать

Пример 1

take занимает столько, сколько он может получить

take(10, range( {from: 100, step: 5, to: 120} ) )

возвращается

[100, 105, 110, 115, 120]

Пример 2

to не обязательно

take(10, range( {from: 100, step: 5} ) )

возвращается

[100, 105, 110, 115, 120, 125, 130, 135, 140, 145]

взять все

Пример 3

from не обязательно

takeAll( range( {to: 5} ) )

возвращается

[0, 1, 2, 3, 4, 5]

Пример 4

takeAll( range( {to: 500, step: 100} ) )

возвращается

[0, 100, 200, 300, 400, 500]

Пример 5

takeAll( range( {from: 'z', to: 'a'} ) )

возвращается

["z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n", "m", "l", "k", "j", "i", "h", "g", "f", "e", "d", "c", "b", "a"]


Отредактировано с моими предложениями :)
Xotic750

+1 за подход. С точки зрения @ alex, отсутствие в предложении троичных операций (особенно не вложенных) forулучшило бы удобочитаемость.
Джастин Джонсон

5

... больше диапазона, используя функцию генератора.

function range(s, e, str){
  // create generator that handles numbers & strings.
  function *gen(s, e, str){
    while(s <= e){
      yield (!str) ? s : str[s]
      s++
    }
  }
  if (typeof s === 'string' && !str)
    str = 'abcdefghijklmnopqrstuvwxyz'
  const from = (!str) ? s : str.indexOf(s)
  const to = (!str) ? e : str.indexOf(e)
  // use the generator and return.
  return [...gen(from, to, str)]
}

// usage ...
console.log(range('l', 'w'))
//=> [ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' ]

console.log(range(7, 12))
//=> [ 7, 8, 9, 10, 11, 12 ]

// first 'o' to first 't' of passed in string.
console.log(range('o', 't', "ssshhhooooouuut!!!!"))
// => [ 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 't' ]

// only lowercase args allowed here, but ...
console.log(range('m', 'v').map(v=>v.toUpperCase()))
//=> [ 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V' ]

// => and decreasing range ...
console.log(range('m', 'v').map(v=>v.toUpperCase()).reverse())

// => ... and with a step
console.log(range('m', 'v')
          .map(v=>v.toUpperCase())
          .reverse()
          .reduce((acc, c, i) => (i % 2) ? acc.concat(c) : acc, []))

// ... etc, etc.

Надеюсь, это полезно.


5

Мой коллега по кегельфингу придумал это (ES6) включительно:

(s,f)=>[...Array(f-s+1)].map((e,i)=>i+s)

не включительно:

(s,f)=>[...Array(f-s)].map((e,i)=>i+s)


4

У d3 также есть встроенная функция дальности. См. Https://github.com/mbostock/d3/wiki/Arrays#d3_range :

d3.range ([start,] stop [, step])

Создает массив, содержащий арифметическую прогрессию, аналогичный встроенному диапазону Python. Этот метод часто используется для перебора последовательности числовых или целочисленных значений, таких как индексы в массиве. В отличие от версии Python, аргументы не обязательно должны быть целыми числами, хотя результаты более предсказуемы, если они обусловлены точностью с плавающей запятой. Если шаг пропущен, по умолчанию он равен 1.

Пример:

d3.range(10)
// returns [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Я никогда не знал, что D3 существует. Не собираюсь использовать их метод диапазона, но буду использовать этот пакет.
mjwrazor

Огромное спасибо. Я использую D3 и искал собственный метод JS, не зная, что я D3 уже предлагает его.
Цезарь

4

Завершите реализацию ES6, используя подпись range ([start,] stop [, step]):

function range(start, stop, step=1){
  if(!stop){stop=start;start=0;}
  return Array.from(new Array(int((stop-start)/step)), (x,i) => start+ i*step)
}

Если вы хотите автоматический отрицательный шаг, добавьте

if(stop<start)step=-Math.abs(step)

Или более минималистично:

range=(b, e, step=1)=>{
  if(!e){e=b;b=0}
  return Array.from(new Array(int((e-b)/step)), (_,i) => b<e? b+i*step : b-i*step)
}

Если у вас огромные диапазоны, посмотрите на генераторный подход Паоло Моретти


Замените !stopна typeof stop === 'undefined', затем замените intна Math.floorи добавьте проверку if (start > stop && step > 0)(в противном случае range(-3, -10)выдает исключение вместо того, чтобы делать что-то вменяемое (либо щелкает знак шага, либо возвращается [])). В противном случае, хорошо!
Ахмед Фасих

4

Там в модуль НПМ Bereich для этого ( «Bereich» является немецкое слово «диапазон»). Он использует современные итераторы JavaScript, поэтому вы можете использовать его различными способами, такими как:

console.log(...bereich(1, 10));
// => 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

const numbers = Array.from(bereich(1, 10));
// => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

for (const number of bereich(1, 10)) {
  // ...
}

Он также поддерживает нисходящие диапазоны (путем простого обмена minи max), а также поддерживает шаги, отличные от 1.

Отказ от ответственности: я являюсь автором этого модуля, поэтому, пожалуйста, примите мой ответ с недоверием.


4

Этот работает также в обратном порядке.

const range = ( a , b ) => Array.from( new Array( b > a ? b - a : a - b ), ( x, i ) => b > a ? i + a : a - i );

range( -3, 2 ); // [ -3, -2, -1, 0, 1 ]
range( 1, -4 ); // [ 1, 0, -1, -2, -3 ]
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.