JavaScript-Funktion ähnlich wie Python range ()

93

Gibt es in JavaScript eine ähnliche Funktion wie Python range()?

Ich denke, es sollte einen besseren Weg geben, als jedes Mal die folgenden Zeilen zu schreiben:

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}
Clwen
quelle
1
@clwen: Leider gibt es keine, aber schauen Sie sich meinen Code an - ich habe eine Funktion geschrieben, die darauf abzielt, die Funktionsweise range()in Python zu emulieren , damit Sie sie verwenden können. In JavaScript gibt es keine solche Funktion, aber es gibt einige Plugins für verschiedene Frameworks, z. B. die RangeKlasse für MooTools .
Tadeck

Antworten:

89

Nein , es gibt keine, aber Sie können eine machen .

JavaScript-Implementierung von Python range()

Beim Versuch zu emulieren, wie es in Python funktioniert, würde ich eine ähnliche Funktion erstellen:

function range(start, stop, step) {
    if (typeof stop == 'undefined') {
        // one param defined
        stop = start;
        start = 0;
    }

    if (typeof step == 'undefined') {
        step = 1;
    }

    if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
        return [];
    }

    var result = [];
    for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
        result.push(i);
    }

    return result;
};

Siehe diese jsfiddle für einen Beweis.

Vergleich zwischen range()in JavaScript und Python

Es funktioniert folgendermaßen:

  • range(4)kehrt zurück [0, 1, 2, 3],
  • range(3,6)kehrt zurück [3, 4, 5],
  • range(0,10,2)kehrt zurück [0, 2, 4, 6, 8],
  • range(10,0,-1)kehrt zurück [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
  • range(8,2,-2)kehrt zurück [8, 6, 4],
  • range(8,2)kehrt zurück [],
  • range(8,2,2)kehrt zurück [],
  • range(1,5,-1)kehrt zurück [],
  • range(1,5,-2)kehrt zurück [],

und sein Python-Gegenstück funktioniert genauso (zumindest in den genannten Fällen):

>>> range(4)
[0, 1, 2, 3]
>>> range(3,6)
[3, 4, 5]
>>> range(0,10,2)
[0, 2, 4, 6, 8]
>>> range(10,0,-1)
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> range(8,2,-2)
[8, 6, 4]
>>> range(8,2)
[]
>>> range(8,2,2)
[]
>>> range(1,5,-1)
[]
>>> range(1,5,-2)
[]

Wenn Sie also eine Funktion benötigen, die ähnlich wie bei Python funktioniert range(), können Sie die oben genannte Lösung verwenden.

Tadeck
quelle
4
Vielleicht ein paar zusätzliche Abwehrprüfungen - stellen Sie sicher, dass die übergebenen Argumente alle für Zahlen erzwungen werden können, und stellen Sie sicher, dass diese stopgrößer sind als start(und tauschen Sie sie aus, wenn nicht).
Russ Cam
1
@RussCam: Danke, dass du darauf hingewiesen hast. Ich habe keine defensiven Prüfungen für Typen usw. hinzugefügt, aber die umgekehrte Reihenfolge der Elemente implementiert - es funktioniert jetzt genauso wie das Python-Gegenstück, wenn der letzte Parameter eine negative Ganzzahl ist.
Tadeck
@RussCam: Das start >= stopFühren zu einem leeren Array ist notwendig, wenn das Ziel wirklich die Reichweite von Python emuliert. Und ich würde behaupten, es ist sowieso intuitiver.
1
@delnan: Die Prüfung ist möglicherweise komplexer, da start >= stopdiese Funktion nicht ausreicht, um sich wie range()in Python zu verhalten . Ich habe meine Antwort aktualisiert.
Tadeck
@delnan - Ich bin mit der Python-Implementierung nicht vertraut. Ich denke, wenn es nur von Leuten verwendet wird, die mit der Python-Implementierung vertraut sind, ist es sinnvoll, es zu emulieren :)
Russ Cam
118

Für einen sehr einfachen Bereich in ES6:

let range = n => Array.from(Array(n).keys())

Aus dem Kommentar von bigOmega geht hervor , dass dies mithilfe der Spread-Syntax verkürzt werden kann :

let range = n => [...Array(n).keys()]
Will Ediger
quelle
40
Eine einfachere Version davon istlet range = n => [...Array(n).keys()]
bigOmega
2
@ BharathRaja toll, danke! Warum nicht const? Ich werde versuchen, const zu verwenden, wo immer ich kann :) wie das letzte Java-Gegenstück;)
Kjellski
Du hast recht, ich versuche constso viel wie möglich zu verwenden, aber hier wird es nur ein Verweis auf das Array sein, so dass das Array immer noch bearbeitet werden kann: 'D
bigOmega
1
@bigOmega Das constwürde für die Funktion selbst gelten, nicht für ihren Rückgabewert. Sie möchten die Funktion wahrscheinlich in keiner Weise ändern.
Solomon Ucko
6
Diese Antwort berücksichtigt keinen Startindex und die Möglichkeit, die Schrittgröße zu erhöhen.
Fluous
28

2018: Diese Antwort erhält immer mehr Stimmen. Hier ist ein Update. Der folgende Code ist veraltet, aber glücklicherweise standardisierte ES6-Generatoren und das yieldSchlüsselwort und sie werden plattformübergreifend universell unterstützt. Ein Beispiel für die faule range()Verwendung yieldfinden Sie hier .


Zusätzlich zu dem, was bereits gesagt wurde, bietet Javascript 1.7+ Unterstützung für Iteratoren und Generatoren, mit denen eine faule, speichereffiziente Version von rangeähnlich wie xrangein Python2 erstellt werden kann:

function range(low, high) {  
    return {
        __iterator__: function() {
            return {  
                next: function() {
                    if (low > high)
                        throw StopIteration;  
                    return low++;
                }
            }
        }
    }
}

for (var i in range(3, 5))  
  console.log(i); // 3,4,5
georg
quelle
1
+1 Tolle Idee! Könnten Sie auch ein stepArgument implementieren und es anhand der Werte aus meiner Antwort testen ? Ihre Antwort eignet sich hervorragend für Anwendungen, bei denen wir ganz bestimmte Browser im Auge haben (sie funktionieren in Google Chrome- , Safari- und IE-Versionen vor 9 nicht: stackoverflow.com/a/2209743/548696 ).
Tadeck
@Tadeck: Ironischerweise habe ich kürzlich eine sehr ähnliche Frage gestellt , probieren Sie es aus - einige gute Antworten dort. Übrigens, Ihr Code besteht meinen Test nicht; (
Georg
Könnten Sie die Testdaten und erwarteten Ergebnisse teilen? Ich würde es gerne verbessern, aber meine Tests bestehen 100%. Wollen Sie damit sagen, dass der von mir angegebene Code von dem Skript, das Sie in diese Frage gestellt haben, nicht richtig analysiert wurde: stackoverflow.com/q/12173856/548696 ?
Tadeck
@ Tadeck: vergiss es. Ich habe Slices getestet und Ihr Code ist für Bereiche.
georg
1
Bei Python rangeist der "letzte" Wert ausgeschlossen (daher muss er >=anstelle des >obigen Codes verwendet werden)
Pac0
21

Beide Antworten von @Tadeck und zusammenführenIch habe @georg mir ausgedacht :

function* range(start, stop, step = 1) {
    if (stop == null) {
        // one param defined
        stop = start;
        start = 0;
    }

    for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
        yield i;
    }
}

Um es in einer for-Schleife zu verwenden, benötigen Sie die for-of-Schleife von ES6 / JS1.7:

for (let i of range(5)) {
    console.log(i);
}
// Outputs => 0 1 2 3 4

for (let i of range(0, 10, 2)) {
    console.log(i);
}
// Outputs => 0 2 4 6 8

for (let i of range(10, 0, -2)) {
    console.log(i);
}
// Outputs => 10 8 6 4 2
janka102
quelle
Warum nicht Standardparameter verwenden, wenn Sie ES6 verwenden?
Amin NAIRI
@Gradiuss das würde für den stepParameter funktionieren , aber wenn stopnicht übergeben wird, müssen beide startund stopmüssen geändert werden. Ich werde es aktualisieren
janka102
2
WTG, dies ist die beste bisher vorgestellte Implementierung. Es verwendet Generatoren (gut, da nicht die gesamte Sequenz im Speicher gespeichert werden muss) und ist sehr prägnant.
Lucio Paiva
undefined ist ein Zeiger auf ein Objekt wie null. Vergleich mit 3 Gleichheitszeichen möglich wie: if (stop === undefined) {3 Gleichheitszeichen wird ohne automatisches Casting verglichen. Vergleichen wie auch Vergleichstyp. 2 Gleichheitszeichen werden mit dem automatischen Casting auf einen anderen Seitentyp verglichen.
Shimon Doodkin
19

Ein Port der rangeFunktion aus Python 2 wird von den Dienstprogrammbibliotheken underscore.js und lodash (zusammen mit vielen anderen nützlichen Tools) bereitgestellt . Beispiele, die aus den Unterstrichdokumenten kopiert wurden:

_.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]
_.range(0);
=> []
Mark Amery
quelle
14

Kann durch Anhängen eines Iterators an den NumberPrototyp erreicht werden

  Number.prototype[Symbol.iterator] = function* () { 
     for (var i = 0; i <= this; i++) {
       yield i
     } 
  }

[...5] // will result in [0,1,2,3,4,5]

Entnommen aus Kyle Simpsons Kurs Asynchrones JavaScript überdenken

mcha
quelle
7
Das ist wirklich cool, aber das Überschreiben von Prototypen ist einfach zu fragil: '(
m0meni
8

Hier ist eine kleine Erweiterung für eine der Antworten, falls Sie sowohl die Start- als auch die Endposition des Bereichs angeben müssen:

let range = (start, end) => Array.from(Array(end + 1).keys()).slice(start);
Dmitrii Mikhailov
quelle
7

Bitte schön.

Dadurch wird der Wert jedes Index mit der Indexnummer geschrieben (oder überschrieben).

Array.prototype.writeIndices = function( n ) {
    for( var i = 0; i < (n || this.length); ++i ) this[i] = i;
    return this;
};

Wenn Sie keine Nummer angeben, wird die aktuelle Länge des Arrays verwendet.

Verwenden Sie es so:

var array = [].writeIndices(10);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
RightSaidFred
quelle
5

Um eine Reihe von Größen zu erhalten x, finden Sie hier einen Einzeiler ohne Verwendung einer Bibliothek

var range = n => Array(n + 1).join(1).split('').map((x, i) => i)

arbeitet als

> range(4)
[0, 1, 2, 3]
bigOmega
quelle
1
var range = n => Array(n).fill().map((e, i) => i);
Valen
und warum 'Größe x' sagen, wenn Sie tatsächlich nals Parametername verwendet haben
Valen
5

Das Folgende ist eine natürliche Anpassung der Range () - Funktion von Python an JavaScript:

// Generate range from start (inclusive) to stop (exclusive):
function* range(start, stop, step = 1) {
   if (stop === undefined) [start, stop] = [0, start];
   if (step > 0) while (start < stop) yield start, start += step;
   else if (step < 0) while (start > stop) yield start, start += step;
   else throw new RangeError('range() step argument invalid');
} 

// Examples:
console.log([...range(3)]);       // [0, 1, 2]
console.log([...range(0, 3)]);    // [0, 1, 2]
console.log([...range(0, 3, -1)]);// []
console.log([...range(0, 0)]);    // []
console.log([...range(-3)]);      // []
console.log([...range(-3, 0)]);   // [-3, -2, -1]

Es unterstützt jedes Argument , das verglichen werden kann 0undstop und kann erhöht werden step. Es verhält sich identisch mit der Python-Version, wenn es mit Zahlen verwendet wird, die nicht größer sindNumber.MAX_SAFE_INTEGER .

Bitte beachten Sie die folgenden Eckfälle:

[...range(0, 0, 0)];        // RangeError: range() step argument invalid
[...range(Number.MAX_SAFE_INTEGER + 1, Number.MAX_SAFE_INTEGER + 2)];  // []
[...range(Number.MAX_SAFE_INTEGER + 2, Number.MAX_SAFE_INTEGER + 3)];  // Infinite loop
[...range(0.7, 0.8, 0.1)];  // [0.7, 0.7999999999999999]
[...range('1', '11')];      // ['1']
[...range('2', '22')];      // Infinite loop

Im Gegensatz zu @ Tadeck des , @ Volv ist und @ janka102 die Antwort , die Rückkehr [], undefinedoder eine Endlosschleife eingeben , wenn stepausgewertet 0oder NaNdiese Generatorfunktion eine Ausnahme ähnlich wie Python Verhalten wirft.

le_m
quelle
Einverstanden. Obwohl die anderen Antworten auf ihre Weise elegant sind, ist dieser Ansatz und diese Funktionalität viel pythonischer.
Travis Clarke
4

Weiter verfeinert mit ES6-Standardparametern.

let range = function*(start = 0, stop, step = 1) {
  let cur = (stop === undefined) ? 0 : start;
  let max = (stop === undefined) ? start : stop;
  for (let i = cur; step < 0 ? i > max : i < max; i += step)
    yield i
}
Volv
quelle
So sollte der Bereich wirklich aussehen.
Konrad Linkowski
4

pythonicahmt die Python rangeVerhalten am besten Generatoren ‚JS verwenden kann ( yield), sowohl die Unterstützung range(stop)und range(start, stop, step)Anwendungsfälle. Darüber hinaus pythonic‚s rangegibt Funktion ein IteratorObjekt ähnlich wie Python , dass Träger mapund filter, so man Lust haben könnte Einzeiler wie:

import {range} from 'pythonic';
// ...
const results = range(5).map(wouldBeInvokedFiveTimes);
// `results` is now an array containing elements from
// 5 calls to wouldBeInvokedFiveTimes

Installieren Sie mit npm:

npm install --save pythonic

Offenlegung Ich bin Autor und Betreuer von Pythonic

Keyvan
quelle
2

Sie können die Unterstrichbibliothek verwenden. Es enthält Dutzende nützlicher Funktionen für die Arbeit mit Arrays und vieles mehr.

Radagast
quelle
2

Gibt es in JavaScript eine Funktion, die Pythons range () ähnelt?

Alle hier aufgeführten Lösungen beziehen sich auf den Bereich von Python 2 (wahrscheinlich aufgrund des von Ihnen angegebenen Codebeispiels). In Python 3 gibt die range () -Methode jedoch einen Iterator zurück. JavaScript hat auch Iteratoren und sie sind platzsparender als das Generieren des gesamten Arrays und das Speichern im Speicher.

Die genauere Darstellung der range(n)Funktion von Python 3 ist also Array(n).keys().

Beispielsweise:

for (let i of Array(n).keys()) {
  console.log(i) // 0, 1, 2, 3, ..., n
}

Ein weiteres Beispiel (das bereits in den anderen Antworten behandelt wurde). Konvertieren des Iterators in ein Array (ES6):

let ary = [...Array(n).keys()];
// ary = [0, 1, 2, 3, ..., n]
MattCochrane
quelle
0

Hier ist eine weitere es6Implementierung des Sortiments

// range :: (from, to, step?) -> [Number]
const range = (from, to, step = 1) => {
  //swap values if necesery
  [from, to] = from > to ? [to, from] : [from, to]
  //create range array
  return [...Array(Math.round((to - from) / step))]
    .map((_, index) => {
      const negative = from < 0 ? Math.abs(from) : 0
      return index < negative ? 
        from + index * step  :
        (index - negative + 1) * step
    })
}  

range(-20, 0, 5)
  .forEach(val => console.log(val))

for(const val of range(5, 1)){
   console.log(`value ${val}`)
}

mrFunkyWisdom
quelle
Was ist, wenn wir ein Array von -20bis generieren möchten -30?
Amin NAIRI
Hmm, dieser generiert ein Array von -30 bis -20, kann aber leicht geändert werden, um eine umgekehrte Eigenschaft einzuschließen, wenn Sie möchten. Ich werde die Antwort oben bearbeiten, um sie aufzunehmen
mrFunkyWisdom
0

Nein, es gibt keine, aber Sie können eine machen.

Ich bin Teil des Python3-Verhaltens der Reichweite. Nachfolgend finden Sie die JavaScript-Implementierung von Pythons range ():

function* range(start=0, end=undefined, step=1) {    
    if(arguments.length === 1) {end = start, start = 0}    
    
    [...arguments].forEach(arg => {    
        if( typeof arg !== 'number') {throw new TypeError("Invalid argument")}                               
    })    
    if(arguments.length === 0) {throw new TypeError("More arguments neede")}    
        
    if(start >= end) return                                                                                                                                     
    yield start    
    yield* range(start + step, end, step)    
}    
         
// Use Cases
console.log([...range(5)])

console.log([...range(2, 5)])

console.log([...range(2, 5, 2)])
console.log([...range(2,3)])
// You can, of course, iterate through the range instance.

elayira
quelle
0

Angenommen, Sie benötigen einen einfachen Bereich mit einem einzigen Schritt:

let range = (start, end)=> {
    if(start === end) return [start];
    return [start, ...range(start + 1, end)];
}

sonst

let range = (start, end, step)=> {
    if(start === end) return [start];
    return [start, ...range(start + step, end)];
}

Weitere Informationen finden Sie hier .

N Djel Okoye
quelle
0

Gibt es in JavaScript eine Funktion, die Pythons range () ähnelt?

Wie zuvor beantwortet: Nein , gibt es nicht. Aber du kannst deine eigenen machen. Ich glaube, dies ist ein interessanter Ansatz für ES6. Es funktioniert sehr ähnlich wie Python 2.7 range(), ist aber viel dynamischer.

function range(start, stop, step = 1) 
{
    // This will make the function behave as range(stop)
    if(arguments.length === 1)
    {
        return [...Array(arguments[0]).keys()]
    }

    // Adjusts step to go towards the stop value
    if((start > stop && !(step < 0)) ||
       (start < stop && !(step > 0)))
    {
        step *= -1
    }

    let returnArray = []
    // Checks if i is in the interval between start and stop no matter if stop
    // is lower than start or vice-versa
    for(let i = start; (i-start)*(i-stop) <= 0; i += step)
    {
        returnArray.push(i)
    }
    return returnArray
}

Diese Funktion kann sich auf drei verschiedene Arten verhalten (genau wie Pythons range ()):

  1. range(stop)
  2. range(start, stop)
  3. range(start, stop, step)

Diese Beispiele:

console.log(range(5))
console.log(range(-2, 2))
console.log(range(2, -2))
console.log(range(10, 20, 2))

Gibt Ihnen die folgende Ausgabe:

[ 0, 1, 2, 3, 4 ]
[ -2, -1, 0, 1, 2 ]
[ 2, 1, 0, -1, -2 ]
[ 10, 12, 14, 16, 18, 20 ]

Beachten Sie, dass Sie anstatt mit dem inOperator (wie Python) über das Array zu iterieren , verwenden müssen of. Daher nimmt die iVariable den Wert und nicht den Index des Array-Elements an.

for(let i of range(5))
{
    // do something with i...
}
sndmnn
quelle
0

Immer noch keine integrierte Funktion, die gleichwertig ist range(), aber mit der neuesten Version - ES2015 - können Sie Ihre eigene Implementierung erstellen. Hier ist eine limitierte Version davon. Eingeschränkt, da der Schrittparameter nicht berücksichtigt wird. Nur min, max.

const range = (min = null, max = null) =>
  Array.from({length:max ? max - min : min}, (v,k) => max ? k + min : k)

Dies wird durch die Array.fromMethode erreicht, mit der ein Array aus jedem Objekt mit einer lengthEigenschaft erstellt werden kann. lengthWenn Sie also ein einfaches Objekt nur mit der Eigenschaft übergeben, wird ein ArrayIterator erstellt, der die lengthAnzahl der Objekte ergibt .

Steve Brownlee
quelle
0

MDN empfiehlt diesen Ansatz: Sequenzgenerator (Bereich)

// Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));

// Generate numbers range 0..4
console.log("range(0, 4, 1):", range(0, 4, 1));
// [0, 1, 2, 3, 4] 

// Generate numbers range 1..10 with step of 2 
console.log("\nrange(1, 10, 2):", range(1, 10, 2));
// [1, 3, 5, 7, 9]

// Generate the alphabet using Array.from making use of it being ordered as a sequence
console.log("\nrange('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x))", range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x)));
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

IliasT
quelle
0

Dies ist mein bevorzugter Weg. Hier können Sie eine oder zwei Eingaben wie in Python angeben.

function range(start, end) {
  return Array.from(Array(end||start).keys()).slice(!!end*start)
}
Rob Kwasowski
quelle
0

Eine Option für NodeJs ist die Verwendung eines Puffers:

[...Buffer.alloc(5).keys()]
// [ 0, 1, 2, 3, 4 ]

Das Schöne ist, dass Sie direkt im Puffer iterieren können:

Buffer.alloc(5).forEach((_, index) => console.log(index))
// 0
// 1
// 2
// 3
// 4

Mit einem nicht initialisierten Array ist das nicht möglich:

Array(5).forEach((_, index) => console.log(index))
// undefined

Aber wer bei klarem Verstand einen Puffer für einen Zweck wie diesen benutzt;)

Otto
quelle
-1

Hier ist, wie ich es mache

let n = 5 
[...Array(n).keys()].map(x=>{console.log(x)})

Ausgabe

0
1
2
3
4
Ricky
quelle
Dies unterstützt keine Schritte.
Mark Stosberg