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

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

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

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

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

Ответ 1

Он работает для символов и цифр, перемещаясь вперед или назад с помощью дополнительного шага.

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.

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;

}

console.log(range("A", "Z", 1));
console.log(range("Z", "A", 1));
console.log(range("A", "Z", 3));


console.log(range(0, 25, 1));

console.log(range(0, 25, 5));
console.log(range(20, 5, 5));

Ответ 2

чисел

[...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)))
}

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

_.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()]);

Ответ 3

Для чисел вы можете использовать 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

Ответ 4

Моя новая любимая форма (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

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

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

Ответ 6

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

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

Ответ 7

Хорошо, в 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"]

Ответ 8

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']

Ответ 9

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

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

Ответ 10

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

Ответ 11

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

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

Пример:

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

Ответ 12

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

Если вы хотите дважды проверить, окончательный ресурс - это стандарт ECMA-262.

Ответ 13

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

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

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

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

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

Ответ 14

Вы можете использовать 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);
}

Ответ 15

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

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

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

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

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

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

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

Ответ 16

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

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

Ответ 17

Я бы закодировал что-то вроде этого:

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]

Ответ 18

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

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

Ответ 19

Хотя это не 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] 

Ответ 20

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

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

Ответ 21

Использование Генераторы гармоник, поддерживается всеми браузерами, кроме 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]

takeAll

Пример 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"]

Ответ 22

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

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]

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

Ответ 23

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

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.

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

Ответ 25

У 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]

Ответ 26

Завершить реализацию ES6 с использованием диапазона ([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)
}

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

Ответ 27

Там модуль НПМ 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.

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

Ответ 28

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

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

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

Ответ 29

Я был удивлен, увидев эту тему и не вижу ничего подобного моему решению (возможно, я пропустил ответ), так что вот оно. Я использую простую функцию диапазона в синтаксисе ES6:

// [begin, end[
const range = (b, e) => Array.apply(null, Array(e - b)).map((_, i) => {return i+b;});

Но он работает только при подсчете вперед (т.е. begin < end), поэтому мы можем немного изменить его при необходимости так:

const range = (b, e) => Array.apply(null, Array(Math.abs(e - b))).map((_, i) => {return b < e ? i+b : b-i;});

Ответ 30

Здесь хороший короткий способ сделать это в ES6 только с номерами (не знаю, насколько скорость его сравнивается):

Array.prototype.map.call(' '.repeat(1 + upper - lower), (v, i) => i + lower)

Для диапазона отдельных символов вы можете немного изменить его:

Array.prototype.map.call(' '.repeat(1 + upper.codePointAt() - lower.codePointAt()), (v, i) => String.fromCodePoint(i + lower.codePointAt()));