Czy JavaScript ma metodę typu „range ()” do generowania zakresu w podanych granicach?

870

W PHP możesz zrobić ...

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

Oznacza to, że istnieje funkcja, która pozwala uzyskać zakres liczb lub znaków, przekraczając górną i dolną granicę.

Czy jest coś wbudowanego w JavaScript w tym celu? Jeśli nie, jak miałbym to wdrożyć?

alex
źródło
1
Prototype.js ma tę $Rfunkcję, ale poza tym tak naprawdę nie sądzę.
Yi Jiang,
To (powiązane) pytanie ma kilka doskonałych odpowiedzi: stackoverflow.com/questions/6299500/...
btk
Array.from("ABC") //['A', 'B', 'C']Jest to najbliższa rzecz, jaką mogę znaleźć w drugiej części pytania.
Andrew_1510
@ Andrew_1510 Można split("")tam również skorzystać
alex
1
Gdy granica kochanka wynosi zero, ten oniner:Array.apply(null, { length: 10 }).map(eval.call, Number)
csharpfolk

Odpowiedzi:

1496

Liczby

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

Iteracja postaci

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

Iteracja

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"

Jako funkcje

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

Jako funkcje wpisane

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()funkcja 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"

Stare przeglądarki inne niż es6 bez biblioteki:

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

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

(Kredyt ES6 dla Nilsa Petersohna i innych komentujących)

Ben
źródło
72
Ponieważ jeśli jest przydatny w dowolnym miejscu, prawdopodobnie jest przydatny w JS. (JS może robić funkcjonalne typy programowania, które mogą korzystać z zakresu (oświadczenie. To i tysiąc innych powodów, które mogą być przydatne w niektórych przypadkach semirare)
Lodewijk
5
Wiesz, dlaczego zwykłe używanie (new Array(5)).map(function (value, index) { return index; })nie działa? To zwraca [undefined × 5]mi w Chrome DevTools.
Lewis,
12
@ Lewis Ponieważ tablica zdefiniowana za pomocą tego ma puste miejsca, które nie będą iterowane z map()ani z jednym z jego przyjaciół.
alex
65
Array.from (Array (5) .keys ())
Nils Petersohn 15.04.2016
17
Array(5).fill()jest również możliwy do zmapowania
nils petersohn
332

W przypadku liczb można użyć ES6 Array.from(), który działa we wszystkim obecnie oprócz IE:

Krótsza wersja:

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

Dłuższa wersja:

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

który tworzy tablicę od 0 do 19 włącznie. Można to dalej skrócić do jednej z tych form:

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

Można również określić dolne i górne granice, na przykład:

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

Artykuł opisujący to bardziej szczegółowo: http://www.2ality.com/2014/05/es6-array-methods.html

Kristjan Liiva
źródło
50
Pierwszy przykład można nawet uprościć do [... Array (20) .keys ()]
Delapouite
27
Nieco bardziej zwięzłe niż Array.from()metoda i szybsze niż oba:Array(20).fill().map((_, i) => i)
Stu Cox
2
@Delapouite Awesome! Powinieneś zrobić z tego osobną odpowiedź, a ja głosuję na nią! To także idealna odpowiedź na ten duplikat .
wysięg
9
@Delapouite @jib I to również:Array.from({length: end - start}, (v, k) => k + start)
Aditya Singh
1
@ icc97 Tak, linters może narzekać, chociaż w JavaScript pominięcie argumentu funkcji zdefiniowanego jako taki sam jak przekazanie undefined, więc fill()(bez argumentu) samo w sobie nie jest błędne . Wartość wypełnienia nie jest używana w tym rozwiązaniu, więc jeśli chcesz, możesz fill(0)zapisać kilka znaków.
Stu Cox,
122

Moja nowa ulubiona forma ( ES2015 )

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

A jeśli potrzebujesz funkcji z stepparametrem:

const range = (start, stop, step = 1) =>
  Array(Math.ceil((stop - start) / step)).fill(start).map((x, y) => x + y * step)
Kutyel
źródło
5
let range = (start, stop, step = 1) => Array (stop - start). fill (start) .map ((x, y) => x + y * step)
rodfersou
4
@rodfersou FYI: twój przykład jest zły. stopnie jest faktycznie pozycją zatrzymania / końca, ale liczbą / odległością. (bez obrazy, tylko po to, by uświadomić ludziom literówkę)
F Lekschas
4
Dla zdezorientowanych - z powodu edycji rodfersou po komentarzu F Lekschasa jego kod jest teraz poprawny.
eedrah
1
Argument, który przekazujesz Array(Math.ceil((stop - start) / step) + 1), potrzebuje +1na końcu, aby naprawdę naśladować zachowanie „integracyjne” php.
Johan Dettmar
3
Jest to najwyższa odpowiedź, która faktycznie odpowiada na pełne pytanie o funkcję JavaScript, która w pełni implementuje rangemetodę. Wszystkie pozostałe powyżej tego (z wyjątkiem lodasha _.range) implementują podstawowe iteratory zamiast rzeczywistej funkcji zakresu z startem, stopem i krokiem
icc97
99

Oto moje 2 centy:

function range(start, count) {
  return Array.apply(0, Array(count))
    .map((element, index) => index + start);
}
jflood.net
źródło
1
Doskonałe wykorzystanie funkcji wysokiego rzędu.
Farzad YZ
5
Jest to w rzeczywistości złe, ponieważ pytanie dotyczy wartości początkowych i końcowych. Nie startuj i nie licz / dystans.
James Robey
73

Działa dla znaków i cyfr, przechodząc do przodu lub do tyłu z opcjonalnym krokiem.

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 .

Jeśli zależy Ci na rozszerzeniu typów natywnych, przypisz je do Array.range.

alex
źródło
53

Prosta funkcja zasięgu:

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

Aby uwzględnić typ danych BitInt, można włączyć sprawdzanie, upewniając się, że wszystkie zmienne są takie same 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;
}

Aby usunąć wartości wyższe niż zdefiniowane stopnp. range(0,5,2)Będzie zawierać 6, co nie powinno być.

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;
}
Remi
źródło
3
PLUS UNO dla użytecznego i czytelnego. Najlepszy fragment kodu, jaki widziałem od dłuższego czasu.
monsto
1
To nie działa, gdy step != 1The whilewarunek musi brać steppod uwagę. Moja zaktualizowana wersja z stepwartością domyślną : zakres funkcji (start, stop, krok) {step = step || 1 var a = [start], b = start; while ((b + step) <stop) {console.log ("b:" + b + ". a:" + a + "."); b + = krok; a.push (b); } zwróć a; }
daveharris,
@daveharris dodałem krok domyślny powyżej, (step || 1).
Pan Polywhirl,
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']
Kennebec
źródło
Naprawdę nie powinieneś używać metod Arrayprototypowania.
connectyourcharger
Ta metoda działa tylko z liczbami całkowitymi i znakami. Jeśli parametry są puste, niezdefiniowane, NaN, boolean, tablica, obiekt itp., Ta metoda zwraca następujący błąd undefined method toUpperCase to etc:!
Victor,
`` `if (typeof from! == 'number' && typeof from! == 'string') {throw new TypeError ('Pierwszym parametrem powinna być liczba lub znak')} if (typeof to! == ' number '&& typeof to! ==' string ') {throw new TypeError (' Pierwszym parametrem powinna być liczba lub znak ')} ``
Victor
36

OK, w JavaScript nie mamy range()funkcji takiej jak PHP , więc musimy stworzyć funkcję, która jest dość łatwa, piszę dla ciebie kilka funkcji jednowierszowych i oddzielam je dla liczb i alfabetów, jak poniżej:

dla liczb :

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

i nazwij to tak:

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

dla alfabetów :

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

i nazwij to tak:

alphabetRange('c', 'h'); //["c", "d", "e", "f", "g"]
Alireza
źródło
2
Myślę, że w tych funkcjach występują błędy popełniane jeden po drugim. Powinien być Array(end - start + 1)i Array(end.charCodeAt(0) - start.charCodeAt(0) + 1).
Earcanal
24

Przydatna funkcja, aby załatwić sprawę, uruchom poniższy fragment kodu

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

oto jak go użyć

zakres (Start, Koniec, Krok = 1, Przesunięcie = 0);

  • włącznie - do przodu range(5,10) // [5, 6, 7, 8, 9, 10]
  • włącznie - wstecz range(10,5) // [10, 9, 8, 7, 6, 5]
  • krok do tyłu range(10,2,2) // [10, 8, 6, 4, 2]
  • wyłączne - do przodu range(5,10,0,-1) // [6, 7, 8, 9] not 5,10 themselves
  • offset - rozwiń range(5,10,0,1) // [4, 5, 6, 7, 8, 9, 10, 11]
  • offset - kurczenie się range(5,10,0,-2) // [7, 8]
  • krok - rozwiń range(10,0,2,2) // [12, 10, 8, 6, 4, 2, 0, -2]

mam nadzieję, że okażą się przydatne.


A oto jak to działa.

Zasadniczo najpierw obliczam długość wynikowej tablicy i tworzę tablicę wypełnioną zerami do tej długości, a następnie wypełniam ją wymaganymi wartościami

  • (step || 1)=> A inne tego typu oznaczają użycie wartości stepi jeśli nie zostało podane, użyj 1zamiast tego
  • Zaczynamy od obliczenia długości tablicy wyników za pomocą, (Math.abs(end - start) + ((offset || 0) * 2)) / (step || 1) + 1)aby ją uprościć (różnica * przesunięcie w obu kierunkach / krokach)
  • Po uzyskaniu długości tworzymy pustą tablicę z zainicjowanymi wartościami, używając new Array(length).fill(0); check tutaj
  • Teraz mamy tablicę o żądanej [0,0,0,..]długości. Mapujemy go i zwracamy nową tablicę z wartościami, których potrzebujemy, używającArray.map(function() {})
  • var direction = start < end ? 1 : 0;Oczywiście, jeśli startnie jest mniejszy niż endmusimy cofnąć się. Mam na myśli przejście od 0 do 5 lub odwrotnie
  • Przy każdej iteracji startingPoint+ stepSize* indexdaje nam potrzebną wartość
azerafati
źródło
8
Z pewnością przydatny. Prosty? Pozwolę sobie być innego zdania; niezależnie od tego, czy robisz z niego jedną wkładkę. Pochodzenie z Pythona to szok.
PascalVKooten
@PascalvKooten, tak, oczywiście byłoby świetnie, gdyby istniała wbudowana metoda dla takich jak python, ale chyba była to najprostsza metoda, jaką mogłem wymyślić. I okazało się przydatne w moich projektach.
azerafati,
Zamieszczanie tak bolesnie złożonego fragmentu kodu, zwłaszcza w postaci jednej linijki i bez wyjaśnienia, jak to działa? Słaby przykład dobrej odpowiedzi SO, niezależnie od tego, czy „działa”.
Madbreaks,
1
@Madbreaks, tak, masz rację. Byłem naiwny, aby uczynić z niego jednego liniowca. chciałem tylko dać wszystkim szybkie i łatwe rozwiązanie
azerafati,
22
var range = (l,r) => new Array(r - l).fill().map((_,k) => k + l);
Klesun
źródło
@nikkwong, _to tylko nazwa argumentu w wywołaniu zwrotnym mapowania. Wiesz, w niektórych językach używałbyś tego _jako nazwy, aby wskazać, że zmienna nie jest używana.
Klesun,
Tutaj jednak _nie przekazuje się argumentów do range. Dlaczego nie?
nikk wong
2
Bardzo schludny! Chociaż należy zauważyć, że nie działa na żadnym IE ani Operze.
Rafael Xavier
4
Ta odpowiedź wymaga wyjaśnienia, ponieważ nie pasuje do SO.
Madbreaks,
@RafaelXavier będzie działał na IE z Array.fill () polyfill
mwag
18

Korzystanie z funkcji rozkładania i strzałek Harmony :

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

Przykład:

range(10, 15);
[ 10, 11, 12, 13, 14, 15 ]
cPu1
źródło
to najlepsza odpowiedź!
Henry H.
1
Jednak nie najszybszy.
mjwrazor
Co w tym przypadku oznacza symbol podkreślenia „_”?
Oleh Berehovskyi
@OlehBerehovskyi Oznacza parametr funkcji lambda, którego nie zamierzasz używać. Linter, który ostrzega przed nieużywanymi zmiennymi, powinien go zignorować.
Micheasz Zoltu,
18

--- AKTUALIZACJA (Dzięki @lokhmakov dla uproszczenia) ---

Kolejna wersja korzystająca z generatorów ES6 (zobacz świetną odpowiedź Paolo Moretti z generatorami ES6 ):

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

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

Lub, jeśli potrzebujemy tylko iteracji, to:

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

--- Kod ORGINAL: ---

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

i

const RANGE_ITER = (a,b) => (function*(x,y){
  while (x <= y) yield x++;
})(a,b);
Hero Qu
źródło
1
Tylko const range = (x, y) => Array.from(function* () { while (x <= y) yield x++; }())
lokhmakov
@lokhmakov Tak, masz rację. Dziękuję Ci! Właśnie zastosowałem twój kod w mojej odpowiedzi.
Hero Qu
15

Przeprowadziłem badania dotyczące różnych funkcji zakresu. Sprawdź porównanie jsperf różnych sposobów wykonywania tych funkcji. Z pewnością nie jest to lista idealna ani wyczerpująca, ale powinna pomóc :)

Zwycięzcą jest...

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

Z technicznego punktu widzenia nie jest to najszybszy firefox, ale nadrabia to szalona różnica prędkości (imho) na chromie.

Ciekawym spostrzeżeniem jest także o ile szybszy jest chrom z tymi funkcjami tablicowymi niż Firefox. Chrome jest co najmniej 4 lub 5 razy szybszy .

Justin
źródło
Zauważ, że porównano to z funkcjami zakresu, które zawierały parametr wielkości kroku
binaryfunt
15

Standardowy Javascript nie ma wbudowanej funkcji do generowania zakresów. Niektóre frameworki javascript dodają obsługę takich funkcji, lub, jak zauważyli inni, zawsze możesz uruchomić własne.

Jeśli chcesz dwukrotnie sprawdzić, ostatecznym zasobem jest standard ECMA-262 .

Mike Dinescu
źródło
Chociaż jestem pewien, że w 2010 roku jest to bardzo dobra odpowiedź, nie należy tego uważać za najlepsze podejście. Nie należy rozszerzać wbudowanych typów, takich jak Prototype.js t
Dana Woodman,
@DanaWoodman dziękuję za poruszenie tej kwestii - zaktualizowałem odpowiedź, aby wziąć pod uwagę odniesienie do Prototype.js, ponieważ jest to rzeczywiście dość przestarzałe w 2018 r.
Mike Dinescu
20
Cóż, to wcale nie pomogło.
Pithikos,
@Pithikos Widzę, że to pytanie było edytowane, ponieważ zostało pierwotnie zadane, a OP chciał wiedzieć, czy w JS jest funkcja zakresu natywnego.
Mike Dinescu
13

Możesz użyć lodash lub Undescore.js range :

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

Alternatywnie, jeśli potrzebujesz tylko kolejnego zakresu liczb całkowitych, możesz zrobić coś takiego:

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

W ES6 rangemożna zaimplementować za pomocą generatorów :

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

Ta implementacja oszczędza pamięć podczas iteracji dużych sekwencji, ponieważ nie musi zmaterializować wszystkich wartości w tablicy:

for (let i of range(1, oneZillion)) {
  console.log(i);
}
Paolo Moretti
źródło
Część ES6 jest teraz poprawną odpowiedzią na to pytanie. Poleciłbym usunięcie innych części, które są objęte innymi odpowiedziami.
dołącza
Generatory są nieco dziwne, jeśli są używane poza pętlą: x = zakres (1, 10); // {} x; // {} // wygląda jak pusta mapa WTF!?! x.next (). wartość; // OK 1; x [3] // niezdefiniowana, tylko z prawdziwą tablicą
Anona112
@ Anona112 można użyć Array.fromdo konwersji generatorów na instancje tablicowe i sprawdzenia danych wyjściowych.
Paolo Moretti
10

Ciekawym wyzwaniem byłoby napisanie najkrótszej funkcji, aby to zrobić. Rekurencja na ratunek!

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

Na dużych zasięgach zwykle działa powoli, ale na szczęście komputery kwantowe są tuż za rogiem.

Dodatkową zaletą jest to, że jest zaciemniające. Ponieważ wszyscy wiemy, jak ważne jest ukrywanie naszego kodu przed wścibskimi oczami.

Aby całkowicie i całkowicie zaciemnić funkcję, wykonaj następujące czynności:

function r(a,b){return (a<b?[a,b].concat(r(++a,--b)):a>b?[]:[a]).sort(function(a,b){return a-b})}
Rick Hitchcock
źródło
4
Krótko! = Proste, ale prostsze jest lepsze. Oto łatwiejsza do odczytania wersja: const range = (a, b) => (a>=b) ? [] : [a, ...range(a+1, b)]przy użyciu składni ES6
nafg
1
@nafg: const range = (a, b, Δ = 1) => (a > b) ? [] : [a, ...range(a + Δ, b, Δ)];. Głosowałem również za odpowiedzią na całą odpowiedź.
7vujy0f0hy
10

To może nie być najlepszy sposób. Ale jeśli chcesz uzyskać zakres liczb w jednym wierszu kodu. Na przykład 10-50

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

Gdzie 40 to (koniec - początek), a 10 to początek. To powinno zwrócić [10, 11, ..., 50]

Edison D'souza
źródło
9

Kodowałbym coś takiego:

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]

Zachowuje się podobnie do zakresu Python:

>>> range(-4,2)
[-4, -3, -2, -1, 0, 1]
Sasha Zezulinsky
źródło
8

Raczej minimalistyczna implementacja mocno wykorzystująca ES6 może zostać utworzona w następujący sposób, zwracając szczególną uwagę na Array.from()metodę statyczną:

const getRange = (start, stop) => Array.from(
  new Array((stop - start) + 1),
  (_, i) => i + start
);
IsenrichO
źródło
Na marginesie, stworzyłem Gist, w którym stworzyłem swego rodzaju „ulepszoną” getRange()funkcję. W szczególności chciałem uchwycić przypadki brzegowe, które mogą być nieradowane w powyższym wariancie bez kości. Dodatkowo dodałem obsługę zakresów alfanumerycznych. Innymi słowy, wywołanie go za pomocą dwóch dostarczonych danych wejściowych, takich jak 'C'i 'K'(w tej kolejności), zwraca tablicę, której wartości to sekwencyjny zestaw znaków od litery „C” (włącznie) do litery „K” (wyłącznie):getRange('C', 'K'); // => ["C", "D", "E", "F", "G", "H", "I", "J"]
IsenrichO
nie potrzebujesz newsłowa kluczowego
Soldeplata Saketos
8

range(start,end,step): Z iteratorami ES6

Pytasz tylko o górną i dolną granicę. Tutaj tworzymy również krok.

Możesz łatwo stworzyć range()funkcję generatora, która może działać jako iterator. Oznacza to, że nie musisz wstępnie generować całej tablicy.

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

Teraz możesz utworzyć coś, co wstępnie generuje tablicę z iteratora i zwraca listę. Jest to przydatne w przypadku funkcji, które akceptują tablicę. Do tego możemy użyćArray.from()

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

Teraz możesz łatwo wygenerować tablicę statyczną,

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

Ale gdy coś wymaga iteratora (lub daje opcję korzystania z iteratora), możesz z łatwością go utworzyć.

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

Specjalne notatki

Evan Carroll
źródło
7

Choć nie jest to z PHP , ale imitacja rangez Pythonem .

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] 

źródło
+1 za najszybszy. z tablicą -36768 - 36768, zajęło 3ms, 2. miejsce zajęło 13 ms i ma czerwone linie IDE.
mjwrazor
7

Jeśli chodzi o generowanie tablicy numerycznej dla danego zakresu, używam tego:

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]

Oczywiście nie będzie działać dla tablic alfabetycznych.

jhaskell
źródło
Ustawienie array = []w pętli może nie dać ci tego, czego chcesz.
alex
@alex, dziękuję. Masz rację, zapomniałem również zwiększać parametr „start” przy każdym przejściu pętli. Teraz jest naprawione.
jhaskell
Nadal nie da pożądanego wyjścia, jeśli chcę zakres 5-10, da mi [5, 6, 7, 8, 9, 10, 11, 12, 13, 14], oczekiwałbym tylko pierwszej połowy tej tablicy.
alex
@alex, jeszcze raz dziękuję, nie rozważałem ograniczenia długości na podstawie danych wejściowych. Zobacz zaktualizowaną wersję.
jhaskell
6

Korzystanie z generatorów Harmony , obsługiwanych przez wszystkie przeglądarki oprócz 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;
        }
    }
});

Przykłady

brać

Przykład 1.

take zajmuje tylko tyle, ile może dostać

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

zwroty

[100, 105, 110, 115, 120]

Przykład 2

to nie jest konieczne

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

zwroty

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

Weź wszystko

Przykład 3

from nie jest konieczne

takeAll( range( {to: 5} ) )

zwroty

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

Przykład 4

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

zwroty

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

Przykład 5

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

zwroty

["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"]

Janus Troelsen
źródło
Edytowane z moimi sugestiami :)
Xotic750
+1 za podejście. Do punktu @ Alexa, brak operacji trójkowych (szczególnie nie zagnieżdżonych) w forklauzuli poprawiłby tutaj czytelność.
Justin Johnson
5

... większy zasięg za pomocą funkcji generatora.

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.

Mam nadzieję, że to się przyda.

John Swindin
źródło
5

Mój współpracujący z nami golfista wymyślił to (ES6), w tym:

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

nie obejmuje:

(s,f)=>[...Array(f-s)].map((e,i)=>i+s)
Marc Sloth Eastman
źródło
4

d3 ma również wbudowaną funkcję zakresu. Zobacz https://github.com/mbostock/d3/wiki/Arrays#d3_range :

d3. zasięg ([start,] stop [, krok])

Generuje tablicę zawierającą postęp arytmetyczny, podobny do zakresu wbudowanego w Pythonie. Ta metoda jest często używana do iteracji po sekwencji wartości liczbowych lub liczb całkowitych, takich jak indeksy w tablicy. W przeciwieństwie do wersji Python argumenty nie muszą być liczbami całkowitymi, chociaż wyniki są bardziej przewidywalne, jeśli wynikają z precyzji zmiennoprzecinkowej. Jeśli krok zostanie pominięty, domyślnie jest to 1.

Przykład:

d3.range(10)
// returns [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Bob Baxley
źródło
Nigdy nie wiedziałem, że D3 istnieje. Nie zamierzam użyć metody zasięgu, ale użyje tego pakietu.
mjwrazor
Dziękuję bardzo. Używam D3 i szukałem natywnej metody JS, nie wiedząc, że I D3 już ją oferuje.
cezar
4

Pełna implementacja ES6 przy użyciu sygnatury zasięgu ([start,] stop [, krok]):

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

Jeśli chcesz automatyczne stopniowanie ujemne, dodaj

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

Lub bardziej minimalistycznie:

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

Jeśli masz ogromny zasięg, spójrz na podejście generatora Paolo Morettiego

Anona112
źródło
Wymień !stopsię typeof stop === 'undefined', a następnie zastąpić intz Math.floor, i dodać czek if (start > stop && step > 0)(w przeciwnym razie, range(-3, -10)zgłasza wyjątek zamiast robić coś Sane (albo przerzucanie znak kroku lub powrotu [])). W przeciwnym razie dobrze!
Ahmed Fasih
4

Jest na to moduł npm bereich („bereich” to niemieckie słowo określające „zasięg”). Wykorzystuje nowoczesne iteratory JavaScript, dzięki czemu można go używać na różne sposoby, takie jak:

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)) {
  // ...
}

Obsługuje również zakresy malejące (po prostu zamieniając mini max), a także obsługuje kroki inne niż1 .

Oświadczenie: Jestem autorem tego modułu, więc proszę o odpowiedź z odrobiną soli.

Golo Roden
źródło
4

Ten działa również w odwrotnej kolejności.

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 ]
Rocco Ghielmini
źródło