Fungsi JavaScript mirip dengan rentang Python ()

96

Apakah ada fungsi dalam JavaScript yang mirip dengan Python range()?

Saya pikir harus ada cara yang lebih baik daripada menulis baris berikut setiap saat:

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}
Clwen
sumber
1
@clwen: Sayangnya tidak ada, tapi lihat kode saya - Saya telah menulis sebuah fungsi yang ditujukan untuk meniru cara range()kerja Python, sehingga Anda dapat menggunakannya. Tidak ada fungsi seperti itu di JavaScript, tetapi ada beberapa plugin untuk kerangka kerja yang berbeda, seperti Rangekelas untuk MooTools .
Tadeck

Jawaban:

89

Tidak , tidak ada, tapi Anda bisa membuatnya .

Implementasi JavaScript dari Python range()

Mencoba meniru cara kerjanya dengan Python , saya akan membuat fungsi yang mirip dengan ini:

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

Lihat jsfiddle ini untuk bukti.

Perbandingan antara range()di JavaScript dan Python

Ini bekerja dengan cara berikut:

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

dan mitra Python-nya bekerja dengan cara yang persis sama (setidaknya dalam kasus yang disebutkan):

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

Jadi jika Anda memerlukan fungsi untuk bekerja mirip dengan Python range(), Anda dapat menggunakan solusi yang disebutkan di atas.

Tadeck
sumber
4
mungkin beberapa pemeriksaan pertahanan tambahan - pastikan bahwa argumen yang diteruskan semuanya dapat dipaksakan untuk angka dan pastikan itu stoplebih besar dari start(dan tukar jika tidak).
Russ Cam
1
@RussCam: Terima kasih telah menunjukkan hal ini. Saya tidak menambahkan pemeriksaan defensif untuk jenis dll., Tetapi saya menerapkan urutan elemen terbalik - sekarang berfungsi persis sama dengan rekan Python, ketika parameter terakhir adalah bilangan bulat negatif.
Tadeck
@RussCam: start >= stopmengarahkan ke array kosong diperlukan jika tujuannya benar-benar meniru jangkauan Python. Dan menurut saya itu lebih intuitif.
1
@delnan: Pemeriksaan mungkin lebih rumit, karena start >= stoptidak cukup sederhana agar fungsi ini berperilaku seperti range()di Python. Saya telah memperbarui jawaban saya.
Tadeck
@ Delnan - Saya tidak terbiasa dengan implementasi Python. Saya kira jika itu hanya akan digunakan oleh orang-orang yang akrab dengan implementasi Python, maka masuk akal untuk menirunya :)
Russ Cam
126

Untuk rentang yang sangat sederhana di ES6:

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

Dari komentar bigOmega , ini dapat dipersingkat menggunakan sintaks Spread :

let range = n => [...Array(n).keys()]
Will Ediger
sumber
41
Versi yang lebih sederhana dari ini adalahlet range = n => [...Array(n).keys()]
bigOmega
2
@BharathRaja bagus, terima kasih! Kenapa tidak const? Saya akan mencoba menggunakan const di mana pun saya bisa :) seperti versi final Java;)
Kjellski
Anda benar, saya mencoba menggunakan constsebanyak mungkin, tetapi di sini hanya akan menjadi referensi ke array, jadi array akan tetap dapat diedit: 'D
bigOmega
1
@bigOmega Itu constakan berlaku untuk fungsi itu sendiri, bukan nilai kembaliannya. Anda mungkin tidak ingin mengubah fungsi dengan cara apa pun.
Solomon Ucko
7
Jawaban ini tidak memperhitungkan indeks awal dan kemampuan untuk meningkatkan ukuran langkah.
Fluous
29

2018: jawaban ini terus mendapatkan suara positif, jadi inilah pembaruannya. Kode di bawah ini sudah usang, tetapi untungnya generator dan yieldkata kunci standar ES6 , dan mereka didukung secara universal di seluruh platform. Sebuah contoh dari malas range()menggunakan yielddapat ditemukan di sini .


Selain apa yang telah disebutkan, Javascript 1.7+ menyediakan dukungan untuk iterator dan generator yang dapat digunakan untuk membuat versi malas, hemat memori range, simlar to xrangedi Python2:

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
sumber
1
+1 Ide bagus! Bisakah Anda menerapkan juga stepargumen dan mengujinya pada nilai-nilai dari jawaban saya ? Jawaban Anda bagus untuk aplikasi yang memiliki browser yang sangat spesifik (tidak akan berfungsi di Google Chrome, Safari, dan versi IE sebelum 9: stackoverflow.com/a/2209743/548696 ).
Tadeck
@Tadeck: ironisnya, saya mengajukan pertanyaan yang sangat mirip baru-baru ini, lihatlah - beberapa jawaban bagus di sana. BTW, kode Anda tidak lulus ujian saya; (
georg
Bisakah Anda membagikan data pengujian dan hasil yang diharapkan? Saya akan dengan senang hati memperbaikinya, tetapi tes saya 100% lulus. Apakah Anda mengatakan bahwa kode yang saya berikan tidak diurai dengan benar oleh skrip yang Anda tempatkan dalam pertanyaan ini: stackoverflow.com/q/12173856/548696 ?
Tadeck
@Tadeck: lupakan. Saya menguji irisan dan kode Anda untuk rentang.
georg
1
Python rangememiliki nilai "terakhir" yang dikecualikan (jadi harus digunakan >=sebagai pengganti >kode di atas)
Pac0
23

Menggabungkan kedua jawaban dari @Tadeck dan @georg , saya menemukan ini:

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

Untuk menggunakannya dalam loop for Anda memerlukan loop for-of 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
sumber
Mengapa tidak menggunakan parameter default jika Anda menggunakan ES6?
Amin NAIRI
@Gradiuss yang akan berfungsi untuk stepparameter, tetapi ketika stoptidak diteruskan, keduanya startdan stopperlu diubah. Saya akan memperbaruinya
janka102
2
WTG, ini adalah implementasi terbaik yang disajikan sejauh ini. Ini menggunakan generator (bagus, karena tidak perlu menyimpan seluruh urutan dalam memori) dan sangat succint.
Lucio Paiva
undefined adalah penunjuk ke objek seperti null. mungkin untuk dibandingkan dengan 3 tanda yang sama seperti: if (stop === undefined) {3 tanda sama dengan dibandingkan tanpa transmisi otomatis. bandingkan seperti juga bandingkan tipe. 2 tanda yang sama dibandingkan dengan pengecoran otomatis ke tipe sisi lain.
Shimon Doodkin
20

Port rangefungsi dari Python 2 disediakan oleh pustaka utilitas underscore.js dan lodash (bersama dengan banyak alat berguna lainnya). Contoh disalin dari dokumen garis bawah:

_.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
sumber
15

Dapat dicapai dengan memasang iterator ke Numberprototipe

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

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

Diambil dari kursus Kyle Simpson Rethinking Asynchronous JavaScript

mcha
sumber
7
Ini benar-benar keren, tetapi prototipe utama terlalu rapuh: '(
m0meni
9

Berikut adalah ekstensi kecil untuk salah satu jawaban jika Anda perlu menentukan posisi awal dan akhir rentang:

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

Ini dia.

Ini akan menulis (atau menimpa) nilai setiap indeks dengan nomor indeks.

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

Jika Anda tidak memberikan nomor, itu akan menggunakan panjang Array saat ini.

Gunakan seperti ini:

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

Untuk mendapatkan berbagai ukuran x, berikut adalah satu baris tanpa menggunakan pustaka apa pun

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

bekerja sebagai

> range(4)
[0, 1, 2, 3]
bigOmega
sumber
2
var range = n => Array(n).fill().map((e, i) => i);
Valen
dan mengapa mengatakan 'ukuran x' ketika Anda benar-benar menggunakan nnama parameter
Valen
5

Berikut ini adalah adaptasi alami dari fungsi range () Python ke 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]

Ini mendukung argumen apa pun yang dapat dibandingkan dengan 0dan stopdan dapat ditambah dengan step. Ini berperilaku identik dengan versi Python saat digunakan dengan angka yang tidak melebihi Number.MAX_SAFE_INTEGER.

Harap perhatikan kasus sudut berikut:

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

Berbeda dengan jawaban @ Tadeck , @ Volv dan @ janka102 yang mengembalikan [], undefinedatau memasuki loop tak terbatas saat stepmengevaluasi ke 0atau NaN, fungsi generator ini menampilkan pengecualian yang mirip dengan perilaku Python.

le_m
sumber
Sepakat. Meskipun jawaban lain elegan dengan caranya sendiri, pendekatan dan fungsionalitas ini jauh lebih pythonic.
Travis Clarke
4

Lebih disempurnakan dengan parameter default ES6.

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
sumber
Seperti inilah seharusnya kisarannya.
Konrad Linkowski
4

pythonicmeniru rangeperilaku Python sebaik mungkin dengan menggunakan generator JS ( yield), mendukung baik kasus penggunaan range(stop)maupun range(start, stop, step)kasus. Selain itu, pythonic's rangefungsi mengembalikan sebuah Iteratorobjek yang sama untuk Python yang mendukung mapdan filter, sehingga orang bisa melakukan mewah satu kalimat seperti:

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

Instal menggunakan npm:

npm install --save pythonic

Pengungkapan Saya penulis dan pengelola Pythonic

Keyvan
sumber
3

MDN merekomendasikan pendekatan ini: Generator urutan (kisaran)

// 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
sumber
2

Anda dapat menggunakan perpustakaan garis bawah . Ini berisi lusinan fungsi yang berguna untuk bekerja dengan array dan banyak lagi.

Radagast
sumber
2

Apakah ada fungsi dalam JavaScript yang mirip dengan range () Python?

Semua solusi di sini mengacu pada kisaran Python 2 (mungkin karena contoh kode yang Anda berikan). Namun dalam Python 3, metode range () mengembalikan sebuah iterator. JavaScript juga memiliki iterator dan mereka lebih hemat ruang daripada menghasilkan seluruh larik dan menyimpannya dalam memori.

Jadi representasi range(n)fungsi Python 3 yang lebih akurat adalah Array(n).keys().

Sebagai contoh:

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

Satu contoh lagi (yang telah tercakup dalam jawaban lain). Mengonversi iterator menjadi array (ES6):

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

Masih belum ada fungsi range()bawaan yang setara , tetapi dengan versi terbaru - ES2015 - Anda dapat membuat implementasi Anda sendiri. Ini versi terbatasnya. Terbatas karena tidak memperhitungkan parameter langkah. Hanya min, maks.

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

Ini dilakukan dengan Array.frommetode yang mampu membangun larik dari objek apa pun yang memiliki lengthproperti. Jadi meneruskan objek sederhana hanya dengan lengthproperti akan membuat ArrayIterator yang akan menghasilkan lengthsejumlah objek.

Steve Brownlee
sumber
1

Ini cara yang saya pilih. Ini memungkinkan Anda untuk menentukan satu atau dua input seperti di Python.

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

Berikut adalah es6implementasi lain dari rentang tersebut

// 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
sumber
Bagaimana jika kita ingin menghasilkan array dari -20to -30?
Amin NAIRI
Hmm, yang ini akan menghasilkan array dari -30 hingga -20, tetapi dapat dengan mudah dimodifikasi untuk menyertakan properti terbalik jika Anda mau. Saya akan mengedit jawaban di atas untuk memasukkannya
mrFunkyWisdom
0

Tidak, tidak ada, tapi Anda bisa membuatnya.

Saya parsial terhadap perilaku jangkauan Python3. Di bawah ini Anda akan menemukan implementasi JavaScript dari range Python ():

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
sumber
0

Dengan asumsi Anda membutuhkan rentang sederhana dengan satu langkah:

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

lain

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

lihat di sini untuk lebih lanjut.

N Djel Okoye
sumber
0

Apakah ada fungsi dalam JavaScript yang mirip dengan range () Python?

Seperti yang telah dijawab sebelumnya: tidak , tidak ada. Tapi Anda bisa membuatnya sendiri. Saya yakin ini adalah pendekatan yang menarik untuk ES6. Cara kerjanya sangat mirip dengan Python 2.7 range(), tetapi jauh lebih dinamis.

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
}

Fungsi ini dapat berperilaku dalam tiga cara berbeda (seperti range ()) Python:

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

Contoh berikut:

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

Akan memberi Anda output berikut:

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

Perhatikan bahwa alih-alih mengulang array dengan inoperator (seperti python), Anda harus menggunakan of. Jadi ivariabel mengasumsikan nilai, dan bukan indeks, dari elemen array.

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

Opsi untuk NodeJs adalah menggunakan Buffer:

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

Yang menyenangkan adalah Anda dapat melakukan iterasi langsung di buffer:

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

Anda tidak dapat melakukannya dengan Array yang tidak diinisialisasi:

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

Tapi, siapa yang waras menggunakan Buffer untuk tujuan seperti ini;)

Otto
sumber
-1

Begini cara saya melakukannya

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

keluaran

0
1
2
3
4
Ricky
sumber
Ini tidak mendukung langkah-langkah.
Mark Stosberg