JavaScript'te bir dizideki öğeleri döndürün


86

Bir JavaScript dizisini döndürmenin en etkili yolunun ne olduğunu merak ediyordum.

Bir pozitifin ndiziyi sağa ve bir negatifin nsola ( -length < n < length) döndürdüğü bu çözümü buldum :

Array.prototype.rotateRight = function( n ) {
  this.unshift( this.splice( n, this.length ) );
}

Hangisi daha sonra bu şekilde kullanılabilir:

var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
months.rotate( new Date().getMonth() );

Yukarıdaki orijinal sürümümün bir kusuru var, Christoph'un aşağıdaki yorumlarda işaret ettiği gibi , doğru bir sürüm (ek dönüş zincirlemeye izin veriyor):

Array.prototype.rotateRight = function( n ) {
  this.unshift.apply( this, this.splice( n, this.length ) );
  return this;
}

Muhtemelen JavaScript çerçevesi bağlamında daha kompakt ve / veya daha hızlı bir çözüm var mı? (aşağıdaki önerilen sürümlerden hiçbiri daha kompakt veya daha hızlı değildir)

Yerleşik bir dizi döndürme özelliğine sahip herhangi bir JavaScript çerçevesi var mı? (Hala kimse tarafından cevaplanmadı)


1
Örneğinizin ne yapması gerektiğini anlamıyorum. Neden months[new Date().getMonth()]içinde bulunduğunuz ayın adını almak için kullanmıyorsunuz ?
Gumbo

1
@Jean: kod bozuldu: bunu yaptığınız şekilde, eklenmiş öğeleri tek tek değil bir dizi olarak değiştirir; apply()uygulamanızı çalıştırmak için kullanmanız gerekecek
Christoph

Bugün rotasyon, bu listeyi göstermek için ayları değiştirecek ( Decilk pozisyonda):["Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov"]
Jean Vincent

@Christoph, haklısın, bu genel bir döndürme işlevi olarak çalışmaz. Yalnızca hemen sonra bir dizeye dönüştürmek için kullanılırsa çalışır.
Jean Vincent

@Jean: Sürümünüzü şu yolla düzeltebilirsiniz Array.prototype.unshift.apply(this, this.splice(...))- benim sürümüm aynı şeyi yapıyor, ancak push()onun yerine kullanıyorunshift()
Christoph

Yanıtlar:


62

Diziyi değiştiren tür güvenli, genel sürüm:

Array.prototype.rotate = (function() {
    // save references to array functions to make lookup faster
    var push = Array.prototype.push,
        splice = Array.prototype.splice;

    return function(count) {
        var len = this.length >>> 0, // convert to uint
            count = count >> 0; // convert to int

        // convert count to value in range [0, len)
        count = ((count % len) + len) % len;

        // use splice.call() instead of this.splice() to make function generic
        push.apply(this, splice.call(this, 0, count));
        return this;
    };
})();

Yorumlarda Jean, kodun push()ve 'nin aşırı yüklenmesini desteklemediği sorununu dile getirdi splice(). Bunun gerçekten yararlı olduğunu düşünmüyorum (yorumlara bakın), ancak hızlı bir çözüm (biraz da olsa hack) bu satırı değiştirmek olacaktır.

push.apply(this, splice.call(this, 0, count));

Bununla birlikte:

(this.push || push).apply(this, (this.splice || splice).call(this, 0, count));

unshift()Bunun yerine kullanmak push()Opera 10'da neredeyse iki kat daha hızlıdır, oysa FF'deki farklılıklar ihmal edilebilir düzeydeydi; kod:

Array.prototype.rotate = (function() {
    var unshift = Array.prototype.unshift,
        splice = Array.prototype.splice;

    return function(count) {
        var len = this.length >>> 0,
            count = count >> 0;

        unshift.apply(this, splice.call(this, count % len, len));
        return this;
    };
})();

2
Array.prototypeYöntemlerin güzel önbelleğe alınması ! +1
James

Çok güzel kurşun geçirmez uygulama, ancak genel olarak kötü kullanım için istisnalara veya sadece kötü yanıtlara güvenmeyi tercih ederim. Bu, kodu temiz ve hızlı tutmak için. Doğru parametreleri geçirmek veya sonuçlarını ödemek kullanıcının sorumluluğundadır. İyi kullanıcılar için cezayı sevmiyorum. Bunun dışında bu mükemmel çünkü Array'i istendiği gibi değiştiriyor ve sizin belirttiğiniz gibi tek tek öğeler yerine bir diziyi değiştiren benim uygulamamdaki kusurdan muzdarip değil. Bunu geri döndürmek, zincirlemeye izin vermek için daha iyidir. Çok teşekkürler.
Jean Vincent

Sadece push ve splice'ı önbelleğe almak için burada kapatmanın maliyeti nedir?
Jean Vincent

@Jean: Kapaklar tüm kapsam zincirini yakalar; dış işlev üst düzey olduğu sürece, etki ihmal edilebilir olmalıdır ve yine de O (1) olurken, Array yöntemlerine bakıldığında çağrı sayısında O (n) olur; uygulamaları optimize etmek, aramayı sıraya sokabilir, böylece herhangi bir kazanç olmaz, ancak uzun süredir uğraşmak zorunda olduğumuz oldukça aptal tercümanlarla, değişkenleri daha düşük bir kapsamda önbelleğe almak önemli bir etkiye sahip olabilir
Christoph

1
Bu, büyük dizileri döndürmez. Bugün kontrol ettim ve yalnızca 250891 öğe uzunluğunda bir dizi işleyebilirdi. Görünüşe göre applyyönteme göre iletebileceğiniz argümanların sayısı belirli bir çağrı yığını boyutuyla sınırlıdır. ES6 açısından, yayma operatörü de aynı yığın boyutu sorunundan muzdariptir. Aşağıda aynı şeyi yapmanın bir harita yöntemi veriyorum. Daha yavaştır ancak milyonlarca ürün için işe yarar. Ayrıca basit bir for veya while döngüsü ile harita uygulayabilirsiniz ve çok daha hızlı olacaktır.
Redu

147

Sen kullanabilirsiniz push(), pop(), shift()ve unshift()yöntem:

function arrayRotate(arr, reverse) {
  if (reverse) arr.unshift(arr.pop());
  else arr.push(arr.shift());
  return arr;
}

kullanım:

arrayRotate(['h','e','l','l','o']);       // ['e','l','l','o','h'];
arrayRotate(['h','e','l','l','o'], true); // ['o','h','e','l','l'];

countTartışmaya ihtiyacınız varsa diğer cevabıma bakın: https://stackoverflow.com/a/33451102 🖤🧡💚💙💜


Kullanıma bağlı olarak çok büyük bir sorun olmasa da bir sayısı yok.
JustGage


2
Sadece bir not: Bu yöntemin müdahaleci olduğuna ve param olarak gönderilen diziyi değiştireceğine dikkat edin. Göndermeden önce diziyi çoğaltmayı
fguillen


38

Muhtemelen şöyle bir şey yapardım:

Array.prototype.rotate = function(n) {
    return this.slice(n, this.length).concat(this.slice(0, n));
}

Düzenle     İşte bir mutatör sürümü:

Array.prototype.rotate = function(n) {
    while (this.length && n < 0) n += this.length;
    this.push.apply(this, this.splice(0, n));
    return this;
}

bu işlevin orijinal diziyi değiştirmediğini unutmayın
Christoph

Aynı push, pop, shift, unshift, concat ve splice'ın yaptığı gibi orijinal Diziyi (bu) değiştirmesi gerekir. Bunun dışında bu geçerlidir.
Jean Vincent

@Gumbo, neden while döngüsü? N pozitif yapmamıza gerek yok, splice negatif değerlerle de çalışıyor. Sonunda, bu doğru ama hemen hemen Christoph versiyonu, aşırı yükleme uyarısı olmadan ilk önce onu doğru yaptı.
Jean Vincent

@Gumbo, önceki yorumumda düzeltme, negatif sayılar sadece splice (n. This.length) versiyonunda çalışıyor. Sizin sürümünüzde olduğu gibi splice (0, n) kullanmak, pozitif bir int gerektirir.
Jean Vincent

1
n = n % this.lengthNegatif ve / veya sınır dışı sayıları işlemek için return ifadesinden önce ekledim .
iedmrc

25

Bu işlev her iki şekilde de çalışır ve herhangi bir sayıyla çalışır (dizi uzunluğundan büyük sayı olsa bile):

function arrayRotate(arr, count) {
  count -= arr.length * Math.floor(count / arr.length);
  arr.push.apply(arr, arr.splice(0, count));
  return arr;
}

kullanım:

for(let i = -6 ; i <= 6 ; i++) {
  console.log(arrayRotate(["🧡","💚","💙","💜","🖤"], i), i);
}

sonuç:

[ "🖤", "🧡", "💚", "💙", "💜" ]    -6
[ "🧡", "💚", "💙", "💜", "🖤" ]    -5
[ "💚", "💙", "💜", "🖤", "🧡" ]    -4
[ "💙", "💜", "🖤", "🧡", "💚" ]    -3
[ "💜", "🖤", "🧡", "💚", "💙" ]    -2
[ "🖤", "🧡", "💚", "💙", "💜" ]    -1
[ "🧡", "💚", "💙", "💜", "🖤" ]    0
[ "💚", "💙", "💜", "🖤", "🧡" ]    1
[ "💙", "💜", "🖤", "🧡", "💚" ]    2
[ "💜", "🖤", "🧡", "💚", "💙" ]    3
[ "🖤", "🧡", "💚", "💙", "💜" ]    4
[ "🧡", "💚", "💙", "💜", "🖤" ]    5
[ "💚", "💙", "💜", "🖤", "🧡" ]    6

Bu işlevi kullanarak bir sorunla karşılaştım çünkü orijinal diziyi değiştiriyor. Burada bazı geçici çözümler buldum: stackoverflow.com/questions/14491405
ognockocaten

1
@ognockocaten Sorun değil, bu işlevin nasıl çalıştığı. orijinal diziyi değiştirmeden tutmak istiyorsanız, daha önce var arr2 = arrayRotate(arr.slice(0), 5)
klonlayın

Bu gerçekten harika bir cevap ve bana yardımcı oldu. Bununla birlikte, orijinal diziyi değiştirmeyen bir alternatif sağlamak en iyisidir. Orijinalin yalnızca bir kopyasını oluşturmak elbette kolaydır, belleği gereksiz yere kullandığından bu en iyi uygulama değildir.
Hibrit web geliştirici

@Hybridwebdev kullanımıconst immutatableArrayRotate = (arr, count) => ArrayRotate(arr.clone(), count)
Yukulélé

9

Bu cevapların çoğu aşırı karmaşık ve okunması zor görünüyor. Concat ile splice kullanan birini gördüğümü sanmıyorum ...

function rotateCalendar(){
    var cal=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
    cal=cal.concat(cal.splice(0,new Date().getMonth()));
    console.log(cal);  // return cal;
}

console.log çıktıları (* Mayıs'ta oluşturulmuştur):

["May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr"]

Kompaktlığa gelince, birkaç genel tek satırlık işlev sunabilirim (console.log | return bölümünü saymaz). Sadece diziyi ve bağımsız değişkenlerdeki hedef değeri besleyin.

Bu işlevleri, dizinin ['N', 'E', 'S', 'W'] olduğu dört oyunculu bir kart oyunu programı için birleştiriyorum. Birinin ihtiyaçları için kopyalamak / yapıştırmak istemesi ihtimaline karşı onları ayrı bıraktım. Amaçlarım için, oyunun farklı aşamalarında (Pinochle) oynamak / hareket etmek için sıranın yanında kimin olduğunu ararken bu işlevleri kullanıyorum. Hız için test yapmakla uğraşmadım, bu yüzden başka biri isterse sonuçları bana bildirmekten çekinmeyin.

* dikkat edin, işlevler arasındaki tek fark "+1" dir.

function rotateToFirst(arr,val){  // val is Trump Declarer's seat, first to play
    arr=arr.concat(arr.splice(0,arr.indexOf(val)));
    console.log(arr); // return arr;
}
function rotateToLast(arr,val){  // val is Dealer's seat, last to bid
    arr=arr.concat(arr.splice(0,arr.indexOf(val)+1));
    console.log(arr); // return arr;
}

kombinasyon işlevi ...

function rotateArray(arr,val,pos){
    // set pos to 0 if moving val to first position, or 1 for last position
    arr=arr.concat(arr.splice(0,arr.indexOf(val)+pos));
    return arr;
}
var adjustedArray=rotateArray(['N','E','S','W'],'S',1);

ayarlananArray =

W,N,E,S

1
Güzel cevap. Karanlık tarafa (PHP) geçmeden önce miydi?
Nick

... karanlık tarafta doğdum.
mickmackusa

:) btw Onu burada
Nick

6

Değişmez bir örnek için ES6'nın yayılmasını kullanmak ...

[...array.slice(1, array.length), array[0]]

ve

[array[array.items.length -1], ...array.slice(0, array.length -1)]

Muhtemelen en verimli değil ama özlü.


5

Dilimleme ve tahrip etme ile kolay çözüm:

const rotate = (arr, count = 1) => {
  return [...arr.slice(count, arr.length), ...arr.slice(0, count)];
};

const arr = [1,2,3,4,5];

console.log(rotate(arr, 1));  // [2, 3, 4, 5, 1]
console.log(rotate(arr, 2));  // [3, 4, 5, 1, 2]
console.log(rotate(arr, -2)); // [4, 5, 1, 2, 3]
console.log(rotate(arr, -1)); // [5, 1, 2, 3, 4]


2
Bu gerçekten güzel! count === 0Varsayılan bir değer ayarlayarak kontrol etme ihtiyacını ortadan kaldırabilirsiniz . Bu, bunu tek satırlık bir hale getirmenize olanak tanır:const rotate = (arr, n = 1) => [...arr.slice(n, arr.length), ...arr.slice(0, n)];
Nick F

4

Bir dizideki öğeleri kaydırmanın çok basit bir yolu:

function rotate(array, stepsToShift) {

    for (var i = 0; i < stepsToShift; i++) {
        array.unshift(array.pop());
    }

    return array;
}

3

@Christoph, temiz bir kod yaptın, ancak bulduğumdan % 60 daha yavaş . JsPerf'deki sonuca bakın: http://jsperf.com/js-rotate-array/2 [Düzenle] Tamam, şimdi daha fazla tarayıcı var ve bariz olmayan cadı yöntemleri en iyisi

var rotateArray = function(a, inc) {
    for (var l = a.length, inc = (Math.abs(inc) >= l && (inc %= l), inc < 0 && (inc += l), inc), i, x; inc; inc = (Math.ceil(l / inc) - 1) * inc - l + (l = inc))
    for (i = l; i > inc; x = a[--i], a[i] = a[i - inc], a[i - inc] = x);
    return a;
};

var array = ['a','b','c','d','e','f','g','h','i'];

console.log(array);
console.log(rotateArray(array.slice(), -1)); // Clone array with slice() to keep original

@molokocolo Sezgisel olarak çözümünüz daha yavaş çalışacaktır çünkü döngü daha yüksek olacaktır. Test durumunuzu 5'lik
Jean Vincent

RotateArray () işlevinin ne yaptığını bilmiyorum ^^ sadece işe yarıyor :) (bu tuhaf bir kod!) Chrome, Firefox'un neredeyse tam tersi gibi davranıyor ...
molokoloco

Bu yöntem çok ilginçtir, dizideki eleman referanslarını değiştirerek çalışır. Dizi boyutu hiçbir zaman değişmez, bu yüzden döngü kullanmanın dezavantajı ile çok hızlıdır. Gösterdiği ilginç şey, Firefox'un döngülerde en hızlısı, Chrome'un dizi işlemlerinde en hızlısı olmasıdır.
Jean Vincent

1
Kodu analiz ettikten sonra, bu çözümün yalnızca daha küçük diziler ve belirli rotasyon sayıları için daha hızlı olduğunu gösteren bir zayıflık buldum: jsperf.com/js-rotate-array/5 Tüm tarayıcılar arasında en hızlısı splice / unshift ile Google Chrome'da kalıyor çözüm. Bu, bunun gerçekten Chrome'un diğer tarayıcılardan daha iyi yaptığı bir Dizi yöntemi optimizasyonu meselesi olduğu anlamına gelir.
Jean Vincent

3

bkz. http://jsperf.com/js-rotate-array/8

function reverse(a, from, to) {
  --from;
  while (++from < --to) {
    var tmp = a[from];
    a[from] = a[to];
    a[to] = tmp;
  }
}

function rotate(a, from, to, k) {
  var n = to - from;
  k = (k % n + n) % n;
  if (k > 0) {
    reverse(a, from, from + k);
    reverse(a, from + k, to);
    reverse(a, from, to);
  }
}

3

'Bugün' ile bir gün listesi başlatmak için hazır bir pasaj bulamadığımda, bunu şöyle yaptım (oldukça genel değil, muhtemelen yukarıdaki örneklerden çok daha az rafine edilmiş, ama işi yaptım):

//returns 7 day names with today first
function startday() {
    const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
    let today = new Date();
    let start = today.getDay(); //gets day number
    if (start == 0) { //if Sunday, days are in order
        return days
    }
    else { //if not Sunday, start days with today
        return days.slice(start).concat(days.slice(0,start))
    }
}

Benden daha iyi bir programcının yaptığı küçük bir yeniden düzenleme sayesinde, bu benim ilk denememden bir veya iki satır daha kısa, ancak verimlilikle ilgili başka yorumlarınız memnuniyetle karşılanır.


3
function rotate(arr, k) {
for (var i = 0; i < k+1; i++) {
    arr.push(arr.shift());
}
return arr;
}
//k work as an index array
console.log(rotate([1, 2, 7, 4, 5, 6, 7], 3)); //[5,6,7,1,2,7,4]
console.log(rotate([-1, -100, 3, 99], 2));     //[99,-1,-100,3]

3
// Example of array to rotate
let arr = ['E', 'l', 'e', 'p', 'h', 'a', 'n', 't'];

// Getting array length
let length = arr.length;

// rotation < 0 (move left), rotation > 0 (move right)
let rotation = 5;

// Slicing array in two parts
let first  = arr.slice(   (length - rotation) % length, length); //['p', 'h', 'a' ,'n', 't']
let second = arr.slice(0, (length - rotation) % length); //['E', 'l', 'e']

// Rotated element
let rotated = [...first, ...second]; // ['p', 'h', 'a' ,'n', 't', 'E', 'l', 'e']

Tek satır kodda:

let rotated = [...arr.slice((length - rotation) % length, length), ...arr.slice(0, (length - rotation) % length)];

2

Kabul edilen yanıt, oturuma bağlı olan ancak yaklaşık 100 ~ 300K öğe gibi olması gereken çağrı yığını boyutundan daha büyük dizileri işleyememe kusuruna sahiptir. Örneğin, denediğim mevcut Chrome oturumunda 250891 idi. Çoğu durumda dizinin dinamik olarak hangi boyuta büyüyebileceğini bile bilmiyor olabilirsiniz. Yani bu ciddi bir problem.

Bu sınırlamanın üstesinden gelmek için ilginç bir yöntem, Array.prototype.map()indisleri döngüsel bir şekilde yeniden düzenleyerek öğeleri kullanmak ve haritalamaktır sanırım . Bu yöntem bir tamsayı argümanı alır. Bu argüman pozitifse, artan indeksler üzerinde ve negatif ise azalan indeks yönünde dönecektir. Bu yalnızca O (n) zaman karmaşıklığına sahiptir ve milyonlarca öğeyi sorunsuz bir şekilde işlerken çağrıldığı diziyi değiştirmeden yeni bir dizi döndürecektir. Nasıl çalıştığını görelim;

Array.prototype.rotate = function(n) {
var len = this.length;
return !(n % len) ? this
                  : n > 0 ? this.map((e,i,a) => a[(i + n) % len])
                          : this.map((e,i,a) => a[(len - (len - i - n) % len) % len]);
};
var a = [1,2,3,4,5,6,7,8,9],
    b = a.rotate(2);
console.log(JSON.stringify(b));
    b = a.rotate(-1);
console.log(JSON.stringify(b));

Aslında iki konuda eleştirildikten sonra şu şekilde;

  1. DRY ihlalini ortaya çıkardığı için pozitif veya negatif girdi için şarta gerek yoktur. Bunu bir harita ile yapabilirsiniz çünkü her negatif n'nin bir pozitif eşdeğeri vardır (Tamamen doğru ..)
  2. Bir Array işlevi ya mevcut diziyi değiştirmeli ya da yeni bir dizi oluşturmalıdır, işleviniz bir kaydırmanın gerekli olup olmadığına bağlı olarak yapabilir (Tamamen doğru ..)

Kodu şu şekilde değiştirmeye karar verdim;

Array.prototype.rotate = function(n) {
var len = this.length;
return !(n % len) ? this.slice()
                  : this.map((e,i,a) => a[(i + (len + n % len)) % len]);
};
var a = [1,2,3,4,5,6,7,8,9],
    b = a.rotate(10);
console.log(JSON.stringify(b));
    b = a.rotate(-10);
console.log(JSON.stringify(b));

Sonra tekrardan; Tabii ki JS functorleri Array.prototype.map(), düz JS'de kodlanmış eşdeğerlerine kıyasla yavaştır. % 100'den fazla performans artışı elde etmek için, denememde Array.prototype.rotate()kullandığım gibi üretim kodundaki bir diziyi döndürmem gerekirse , muhtemelen benim seçimim olacaktır .String.prototype.diff()

Array.prototype.rotate = function(n){
  var len = this.length,
      res = new Array(this.length);
  if (n % len === 0) return this.slice();
  else for (var i = 0; i < len; i++) res[i] = this[(i + (len + n % len)) % len];
  return res;
};

shift () ve splice (), ne kadar yavaş olsalar da aslında map () kullanmaktan veya bir fonksiyon parametresi kullanan herhangi bir yöntemden daha hızlıdır. Daha açıklayıcı olan shift () ve splice (), uzun vadede optimize etmek de daha kolaydır.
Jean Vincent

@Jean Vincent Evet haklısınız .. harita aslında daha yavaş olabilir ama ideal dönme mantığının bu olduğuna inanıyorum. Sadece mantıklı yaklaşımı göstermeye çalıştım. Harita, bir for döngüsü ile kolayca basitleştirilebilir ve hızda önemli bir artışa neden olabilir ... Ancak gerçek şu ki, kabul edilen yanıtın diğer bazı diller göz önünde bulundurularak geliştirilmiş bir yaklaşımı vardır. JS için ideal değildir .... dizi boyutu çağrı yığını boyutundan daha büyük olduğunda bile çalışmaz. (benim durumumda ve bu oturumda sadece 250891 maddeyle sınırlı olduğu ortaya çıktı) İşte bu…!
Redu

Uygulanabilecek çağrı yığını boyutu konusunda haklısınız, bu hızdan daha geçerli bir argümandır, bu noktayı iyileştirilmiş girinti ile vurgulamak için cevabınızı düzenlemeyi düşünün.
Jean Vincent

2

Bu işlev, küçük diziler için kabul edilen yanıttan biraz daha hızlıdır, ancak büyük diziler için ÇOK daha hızlıdır. Bu işlev, aynı zamanda, orijinal işlevin bir sınırlaması olan, dizinin uzunluğundan daha büyük bir keyfi sayıda dönüşe izin verir.

Son olarak, kabul edilen cevap açıklandığı gibi ters yönde döner.

const rotateForEach = (a, n) => {
    const l = a.length;
    a.slice(0, -n % l).forEach(item => a.push( item ));
    return a.splice(n % l > 0 ? (-n % l) : l + (-n % l));
}

Ve işlevsel eşdeğeri (bazı performans avantajları da var gibi görünüyor):

const rotateReduce = (arr, n) => {
    const l = arr.length;
    return arr.slice(0, -n % l).reduce((a,b) => {
        a.push( b );
        return a;
    }, arr).splice(n % l> 0 ? l + (-n % l) : -n % l);
};

Performans dağılımını buradan kontrol edebilirsiniz .


Ne yazık ki işe yaramıyor, diziyi sıfır boyutuna küçültüyor, bu da ilk çalıştırmadan sonra diğer tüm doğru uygulamalardan, özellikle daha büyük dizilerde neden çok daha hızlı olduğunu açıklıyor. Kıyaslama testinizde, testlerin sırasını değiştirirseniz ve sonunda a.length görüntülerseniz, sorunu görürsünüz.
Jean Vincent

2

DÜZENLEME :: Hey, çok fazla yineleme oluyor. Döngü yok, dallanma yok.

Hala herhangi bir n boyutu için sağ dönüş için negatif n ve sol dönüş için pozitif n ile çalışır

function rotate(A,n,l=A.length) {
  const offset = (((n % l) + l) %l)
  return A.slice(offset).concat(A.slice(0,offset))
}

İşte kıkırdamalar için kod golf versiyonu

const r = (A,n,l=A.length,i=((n%l)+l)%l)=>A.slice(i).concat(A.slice(0,i))

EDIT1 :: * Dalsız , mutasyonsuz uygulama.

Öyleyse hey, ihtiyacım olmayan bir şubem olduğu ortaya çıktı. İşte çalışan bir çözüm. negatif num = sağa döndür | num | pozitif num = num ile sola döndür

function r(A,n,l=A.length) {
  return A.map((x,i,a) => A[(((n+i)%l) + l) % l])
}

Denklem ((n%l) + l) % l, herhangi bir keyfi büyük n değerinin tam olarak pozitif ve negatif sayılarını eşler

ORİJİNAL

Sola ve sağa döndürün. Pozitif ile sola n, negatif ile sağa döndürün n.

Müstehcen büyük girdiler için çalışıyor n.

Mutasyon modu yok. Bu cevaplarda çok fazla mutasyon var.

Ayrıca, çoğu yanıttan daha az işlem. Pop yok, itme yok, ekleme yok, vardiya yok.

const rotate = (A, num ) => {
   return A.map((x,i,a) => {
      const n = num + i
      return n < 0 
        ? A[(((n % A.length) + A.length) % A.length)]
        : n < A.length 
        ? A[n] 
        : A[n % A.length]
   })
}

veya

 const rotate = (A, num) => A.map((x,i,a, n = num + i) => 
  n < 0
    ? A[(((n % A.length) + A.length) % A.length)]
    : n < A.length 
    ? A[n] 
    : A[n % A.length])

//test
rotate([...Array(5000).keys()],4101)   //left rotation
rotate([...Array(5000).keys()],-4101000)  //right rotation, num is negative

// will print the first index of the array having been rotated by -i
// demonstrating that the rotation works as intended
[...Array(5000).keys()].forEach((x,i,a) => {
   console.log(rotate(a,-i)[0])
}) 
// prints even numbers twice by rotating the array by i * 2 and getting the first value
//demonstrates the propper mapping of positive number rotation when out of range
[...Array(5000).keys()].forEach((x,i,a) => {
   console.log(rotate(a,i*2)[0])
})

Açıklama:

her bir A dizinini, dizin ofsetindeki değerle eşleyin. Bu durumda

offset = num

eğer o offset < 0zaman offset + index + positive length of Aters ofseti gösterecektir.

eğer offset > 0 and offset < length of Ao zaman mevcut dizini A'nın ofset indeksiyle eşleştirin.

Aksi takdirde, ofseti dizinin sınırları içinde eşlemek için ofseti ve uzunluğu modüle edin.

Örneğin al offset = 4ve offset = -4.

Ne zaman offset = -4ve A = [1,2,3,4,5], her indeks offset + indexiçin, büyüklüğü (veya Math.abs(offset)) küçültecek.

Önce negatif n indeksi için hesaplamayı açıklayalım. A[(((n % A.length) + A.length) % A.length)+0]ve sindirildi. Olma. Anlamak için bir Repl'de 3 dakikamı aldı.

  1. nOlumsuz olduğunu biliyoruz çünkü durum öyle n < 0. Sayı, Dizi'nin aralığından büyükse, n % A.lengthonu aralık ile eşler.
  2. n + A.lengthA.lengthdoğru miktarda denkleştirmek için bu sayıyı ekleyin .
  3. nOlumsuz olduğunu biliyoruz çünkü durum öyle n < 0. doğru miktarda denkleştirmek n + A.lengthiçin bu sayıyı ekleyin A.length.
  4. Sonraki Modulo kullanarak bunu A uzunluğu aralığına eşleyin. İkinci değişken, hesaplamanın sonucunu indekslenebilir bir aralıkla eşlemek için gereklidir

    görüntü açıklamasını buraya girin

  5. İlk indeks: -4 + 0 = -4. A. uzunluk = 5. A. uzunluk - 4 = 1. A 2 , 2. Harita indeksi 0 - 2.[2,... ]

  6. Sonraki dizin, -4 + 1 = -3. 5 + -3 = 2. A 2 , 3. Harita indeksi 1 - 3.[2,3... ]
  7. Vb.

Aynı süreç için de geçerlidir offset = 4. Ne zaman offset = -4ve A = [1,2,3,4,5]her indeks offset + indexiçin büyüklüğü büyütür.

  1. 4 + 0 = 0. A [0] 'ı A [4]' daki değere eşleyin.[5...]
  2. 4 + 1 = 5, 5, indeksleme sırasında sınırların dışındadır, bu nedenle A 2'yi kalanındaki değerle eşleyin 5 / 5, bu değer 0'dır. A 2 = A [0] 'daki değer.[5,1...]
  3. tekrar et.

1
Follow a simpler approach of running a loop to n numbers and shifting places upto that element.

function arrayRotateOne(arr, n) {
  for (let i = 0; i < n; i++) {
    arr.unshift(arr.pop());
  }
  return arr;
}
console.log( arrayRotateOne([1,2,3,4,5,6],2));



function arrayRotateOne(arr,n) {
  for(let i=0; i<n;i++){
      arr.push(arr.shift());
      console.log('execute',arr)
    }
     return arr;
 }

console.log (arrayRotateOne ([1,2,3,4,5,6], 2));


1

Mutasyona uğramayan çözüm

var arr = ['a','b','c','d']
arr.slice(1,arr.length).concat(arr.slice(0,1)

mutasyonla

var arr = ['a','b','c','d']
arr = arr.concat(arr.splice(0,1))

1

Karuseli döndürmek için kullandığım çözümümü paylaşıyorum. Dizi boyutu daha küçük olduğunda bozulabilir displayCount, ancak küçük olduğunda dönmeyi durdurmak için ekstra koşul ekleyebilir veya ana dizi * displayCount zamanlarını da birleştirebilirsiniz.

function rotate(arr, moveCount, displayCount) {
  const size = arr.length;

  // making sure startIndex is between `-size` and `size`
  let startIndex = moveCount % size;
  if (startIndex < 0) startIndex += size; 

  return [...arr, ...arr].slice(startIndex, startIndex + displayCount);
}

// move 3 to the right and display 4 items
// rotate([1,2,3,4,5], 3, 4) -> [4,5,1,2]

// move 3 to the left and display 4 items
// rotate([1,2,3,4,5], -3, 4) -> [3,4,5,1]

// move 11 to the right and display 4
// rotate([1,2,3,4,5], 3, 4) -> [2,3,4,5]

0

Olmanız gereken yere ulaşmak için bir sayacı artırmaya ve ardından dizi uzunluğuna göre bölümün kalanını almaya ne dersiniz?

var i = 0;
while (true);
{
    var position = i % months.length;
    alert(months[position]);
    ++i;
}

Dil sözdizimi bir yana, bunun iyi çalışması gerekir.


2
Bu, Diziyi hiçbir şekilde döndürmez.
Jean Vincent

1
Doğru (cevapta belirtildiği gibi), ancak diziyi döndürmek zorunda kalmadan tasarruf sağlar.
tgandrews

1
Ancak bu IS'nin tüm amacı, belirli bir uzaklıktan bir dizi görüntülemek değil, her başlığa göre bir Diziyi döndürmektir. Yeni diziyi yeniden oluşturmak için aynı döngüyü kullanırsanız, yerel Dizi yöntemlerini kullanan diğer sürümlerden çok daha yavaş olacaktır. Ayrıca sonsuz döngüden çıkmayı unuttunuz.
Jean Vincent

0

Diziniz büyük olacaksa ve / veya çok fazla dönecekseniz, dizi yerine bağlantılı bir liste kullanmayı düşünebilirsiniz.


Kabul edildi, ancak soru Dizilerle ve daha spesifik olarak Dizi fiillerini genişletmekle ilgilidir.
Jean Vincent

0

@molokoloco Bir yönde döndürmek için yapılandırabileceğim bir işleve ihtiyacım vardı - ileri için doğru ve geri için yanlış. Bir yön, bir sayaç ve bir dizi alan ve önceki, geçerli ve sonraki değerlerin yanı sıra uygun yönde artırılmış sayaçla bir nesne çıkaran bir parçacık oluşturdum. Orijinal diziyi DEĞİŞTİRMEZ.

Ben de pasajınıza göre saatledim ve daha hızlı olmasa da, sizinkinden daha hızlı -% 21 daha yavaş http://jsperf.com/js-rotate-array/7 .

function directionalRotate(direction, counter, arr) {
  counter = direction ? (counter < arr.length - 1 ? counter + 1 : 0) : (counter > 0 ? counter - 1 : arr.length - 1)
  var currentItem = arr[counter]
  var priorItem = arr[counter - 1] ? arr[counter - 1] : arr[arr.length - 1]
  var nextItem = arr[counter + 1] ? arr[counter + 1] : arr[0]
  return {
    "counter": counter,
    "current": currentItem,
    "prior": priorItem,
    "next": nextItem
  }
}
var direction = true // forward
var counter = 0
var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];

directionalRotate(direction, counter, arr)

Bu, Diziyi döndürmez. Bu soruyu cevaplamak için, ilk öğenin sayaçta olduğu bir Dizi döndürmeniz gerekir.
Jean Vincent

0

Geç geliyorum ama bu güzel cevaplara ekleyeceğim bir tuğla var. Böyle bir işlevi kodlamam istendi ve önce şunu yaptım:

Array.prototype.rotate = function(n)
{
    for (var i = 0; i < n; i++)
    {
        this.push(this.shift());
    }
    return this;
}

Ancak n, büyük olduğunda takip etmekten daha az verimli görünüyordu :

Array.prototype.rotate = function(n)
{
    var l = this.length;// Caching array length before map loop.

    return this.map(function(num, index) {
        return this[(index + n) % l]
    });
}

0

Bunun en verimli yol olup olmadığından emin değilim ama okuma şeklini seviyorum, üretimde test ettiğim için çoğu büyük görev için yeterince hızlı ...

function shiftRight(array) {
  return array.map((_element, index) => {
    if (index === 0) {
      return array[array.length - 1]
    } else return array[index - 1]
  })
}

function test() {
  var input = [{
    name: ''
  }, 10, 'left-side'];
  var expected = ['left-side', {
    name: ''
  }, 10]
  var actual = shiftRight(input)

  console.log(expected)
  console.log(actual)

}

test()


0

Yerli, hızlı, küçük, anlamsal, eski motorlarda çalışır ve "curryable".

function rotateArray(offset, array) {
    offset = -(offset % array.length) | 0 // ensure int
    return array.slice(offset).concat(
        array.slice(0, offset)
    )
}

0

** JS'nin en son sürümünü kullanarak onu kolayca oluşturabiliriz **

 Array.prototype.rotateLeft = function (n) {
   this.unshift(...this.splice(-(n), n));
    return this
  }

burada hareket eder: döndürme sayısı, rastgele sayı iletebileceğiniz bir Dizi

let a = [1, 2, 3, 4, 5, 6, 7];
let moves = 4;
let output = a.rotateLeft(moves);
console.log("Result:", output)

0

ArrayJS'de, bir diziyi oldukça kolay bir şekilde döndürmek için kullanılabilecek yerleşik bir yöntem vardır ve bu yöntemler doğası gereği değişmezdir .

  • push: Öğeyi dizinin sonuna ekler.
  • pop: Öğeyi dizinin sonundan kaldırır.
  • unshift: Öğeyi dizinin başına ekler.
  • shift: Öğeyi dizinin başından kaldırır.

Aşağıdaki çözüm ( ES6) iki bağımsız değişken alır, dizinin döndürülmesi gerekir ve n, dizinin kaç kez döndürülmesi gerekir.

const rotateArray = (arr, n) => {
  while(arr.length && n--) {
    arr.unshift(arr.pop());
  }
  return arr;
}

rotateArray(['stack', 'overflow', 'is', 'Awesome'], 2) 
// ["is", "Awesome", "stack", "overflow"]

Array.prototype'a eklenebilir ve uygulamanızın tamamında kullanılabilir

Array.prototype.rotate = function(n) {
 while(this.length && n--) {
   this.unshift(this.pop());
 }
 return this;
}
[1,2,3,4].rotate(3); //[2, 3, 4, 1]

0

For döngüsü kullanma. İşte adımlar

  1. Dizinin ilk öğesini geçici değişken olarak saklayın.
  2. Ardından soldan sağa değiştirin.
  3. Ardından dizinin son öğesine geçici değişken atayın.
  4. Döndürme sayısı için bu adımları tekrarlayın.

function rotateLeft(arr, rotations) {
    let len = arr.length;
    for(let i=0; i<rotations; i++){ 
        let temp = arr[0];
        for(let i=0; i< len; i++){
            arr[i]=arr[i+1];
        }
        arr[len-1]=temp;
    }
    return arr;
}

let arr = [1,2,3,4,5];

let rotations = 3;
let output = rotateLeft(arr, rotations);
console.log("Result Array => ", output);



0

Aşağıdakileri kullanın -

arr=[1,2,3,4,5]  
let arrs=[]
arrs=arr.slice(d%arr.length).concat(arr.slice(0,d%arr.length))

Nerede, drotasyon yok
En İyi Çözüm, O (1) zaman karmaşıklığı ile Popping Pushing'in Brute force tekniklerini uygulamaya gerek yok

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.