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ć?


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)


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


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)

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

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.


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

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

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

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ść

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

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

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

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 .


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 .


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

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

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]


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]

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

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


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] 

+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.


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


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.


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)


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]

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


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.


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 ]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.