web-dev-qa-db-fra.com

Quelle est la façon la plus efficace d'inverser un tableau en Javascript?

On m'a récemment demandé quel était le moyen le plus efficace pour inverser un tableau en Javascript. Pour le moment, j'ai suggéré d'utiliser une boucle for et de jouer avec le tableau, mais j'ai réalisé qu'il y avait une méthode native Array.reverse().

Par curiosité, quelqu'un peut-il m'aider à explorer cela en montrant des exemples ou en pointant dans la bonne direction pour que je puisse lire ceci? Toute suggestion sur la façon de mesurer les performances serait également impressionnante.

57
luisgo

Sur la base de cette configuration:

var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var length = array.length;

Array.reverse(); est la première ou la deuxième plus lente!

Les repères sont ici: http://jsperf.com/js-array-reverse-vs- while-loop/5

Dans tous les navigateurs, les boucles de swap sont plus rapides. Il existe deux types courants d'algorithmes de swap (voir Wikipedia ), chacun avec deux variantes.

Les deux types d'algorithmes d'échange sont l'échange temporaire et XOR swap.

Les deux variantes gèrent différemment les calculs d'index. La première variation compare l'index gauche actuel et l'index droit, puis décrémente l'index droit du tableau. La deuxième variation compare l'index gauche actuel et la longueur divisée par deux, puis recalcule l'index droit pour chaque itération.

Vous pouvez ou non voir d'énormes différences entre les deux variantes. Par exemple, dans Chrome 18, les premières variations du swap temporaire et XOR swap sont plus de 60% plus lentes que les secondes variantes, mais dans Opera 12, les deux variantes du swap temporaire et XOR swap ont des performances similaires.

Échange temporaire:

Première variation:

function temporarySwap(array)
{
    var left = null;
    var right = null;
    var length = array.length;
    for (left = 0, right = length - 1; left < right; left += 1, right -= 1)
    {
        var temporary = array[left];
        array[left] = array[right];
        array[right] = temporary;
    }
    return array;
}

Deuxième variante:

function temporarySwapHalf(array)
{
    var left = null;
    var right = null;
    var length = array.length;
    for (left = 0; left < length / 2; left += 1)
    {
        right = length - 1 - left;
        var temporary = array[left];
        array[left] = array[right];
        array[right] = temporary;
    }
    return array;
}

Échange XOR:

Première variation:

function xorSwap(array)
{
    var i = null;
    var r = null;
    var length = array.length;
    for (i = 0, r = length - 1; i < r; i += 1, r -= 1)
    {
        var left = array[i];
        var right = array[r];
        left ^= right;
        right ^= left;
        left ^= right;
        array[i] = left;
        array[r] = right;
    }
    return array;
}

Deuxième variante:

function xorSwapHalf(array)
{
    var i = null;
    var r = null;
    var length = array.length;
    for (i = 0; i < length / 2; i += 1)
    {
        r = length - 1 - i;
        var left = array[i];
        var right = array[r];
        left ^= right;
        right ^= left;
        left ^= right;
        array[i] = left;
        array[r] = right;
    }
    return array;
}

Il existe une autre méthode d'échange appelée assignation de la déstructuration: http://wiki.ecmascript.org/doku.php?id=harmony:destructuring

Mission de déstructuration:

Première variation:

function destructuringSwap(array)
{
    var left = null;
    var right = null;
    var length = array.length;
    for (left = 0, right = length - 1; left < right; left += 1, right -= 1)
    {
        [array[left], array[right]] = [array[right], array[left]];
    }
    return array;
}

Deuxième variante:

function destructuringSwapHalf(array)
{
    var left = null;
    var right = null;
    var length = array.length;
    for (left = 0; left < length / 2; left += 1)
    {
        right = length - 1 - left;
        [array[left], array[right]] = [array[right], array[left]];
    }
    return array;
}

À l'heure actuelle, un algorithme utilisant l'affectation de déstructuration est le plus lent de tous. Il est encore plus lent que Array.reverse();. Cependant, les algorithmes utilisant des affectations de déstructuration et des méthodes Array.reverse(); sont les exemples les plus courts, et ils ont l'air les plus propres. J'espère que leurs performances s'amélioreront à l'avenir.


Une autre mention est que les navigateurs modernes améliorent leurs performances des opérations de tableau Push et splice.

Dans Firefox 10, cet algorithme de boucle for utilisant le tableau Push et splice rivalise avec le swap temporaire et XOR les algorithmes de boucle swap.

for (length -= 2; length > -1; length -= 1)
{
    array.Push(array[length]);
    array.splice(length, 1);
}

Cependant, vous devriez probablement vous en tenir aux algorithmes de boucle d'échange jusqu'à ce que de nombreux autres navigateurs correspondent ou dépassent leurs performances de tableau Push et splice.

72
XP1

Les méthodes natives sont toujours plus rapides.

Utilisez donc Array.reverse Dans la mesure du possible. Sinon, une implémentation qui s'exécute dans O(1) serait la meilleure;)

Sinon, utilisez simplement quelque chose comme ça

var reverse = function(arr) {
   var result = [],
       ii = arr.length;
   for (var i = ii - 1;i !== 0;i--) {
       result.Push(arr[i]);
   }
   return result;
}

Benchmark!

L'intérêt de la boucle est plus rapide si vous utilisez les trois étapes de la construction for au lieu d'une seule.

for(var i = ii - 1; i !== 0;i--) est plus rapide que var i = ii - 1;for(;i-- !== 0;)

17
Raynos

De manière simple, vous pouvez le faire en utilisant la carte.

let list = [10, 20, 30, 60, 90]
let reversedList = list.map((e, i, a)=> a[(a.length -1) -i]) // [90, 60...]
13
Srinivas Damam

I a ouvert un bogue Firefox sur les performances inverses lentes dans Firefox. Quelqu'un de Mozilla a regardé le benchmark utilisé dans le post accepté et dit que c'est assez trompeur - dans leur analyse, la méthode native est généralement meilleure pour inverser les tableaux. (Comme cela devrait être!)

10
starwed

Puisque personne ne l'a inventé et pour compléter la liste des façons d'inverser un tableau ...

array.sort(function() {
    return 1;
})

C'est deux fois plus rapide que les deux en approche, mais à part ça, horriblement lent.

http://jsperf.com/js-array-reverse-vs- while-loop/5

5
CoDEmanX

Les fonctions de swap sont les plus rapides. Voici une fonction inverse que j'ai écrite qui n'est que légèrement similaire aux fonctions de swap mentionnées ci-dessus mais qui fonctionne plus rapidement.

function reverse(array) {
  var first = null;
  var last = null;
  var tmp = null;
  var length = array.length;

  for (first = 0, last = length - 1; first < length / 2; first++, last--) {
    tmp = array[first];
    array[first] = array[last];
    array[last] = tmp;
  }
}

Vous pouvez trouver l'analyse comparative ici http://jsperf.com/js-array-reverse-vs- While-loop/19

3
Brice Lin

C'est la façon la plus efficace et la plus propre d'inverser un tableau avec l'opérateur ternaire.

function reverse(arr) {
  return arr.length < 2 ? arr : [arr.pop()].concat(reverse(arr));
}
console.log(reverse([4, 3, 3, 1]));
3
Arslan shakoor

Voici un Java http://www.leepoint.net/notes-Java/data/arrays/arrays-ex-reverse.html montrant comment inverser un très facile à convertir en javascript.

Je suggère d'utiliser quelque chose qui capture simplement le temps avant l'appel de la fonction et après l'appel de la fonction. Celui qui prend le moins de temps/cycles d'horloge sera le plus rapide.

2
clamchoda

Une autre suggestion, similaire à celle ci-dessus, mais utilisant à la place une épissure:

var myArray=["one","two","three","four","five","six"];
console.log(myArray);
for(i=0;i<myArray.length;i++){
myArray.splice(i,0,myArray.pop(myArray[myArray.length-1]));
}
console.log(myArray);
2
SlavCo Zute

Si vous souhaitez copier une version inversée d'un tableau et conserver l'original tel quel:

a = [0,1,2,3,4,5,6,7,8,9];
b = []
for(i=0;i<a.length;i++){
    b.Push(a.slice(a.length-i-1,a.length-i)[0])
}

Sortie de b:

[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
1
tarikakyol

Voici un autre exemple pour modifier définitivement le tableau en inversant ses éléments:

var theArray = ['a', 'b', 'c', 'd', 'e', 'f'];

function reverseArrayInPlace(array) {
  for (var i = array.length - 1; i >= 0; i -= 1) {
    array.Push(array[i]);
  }
  array.splice(0, array.length / 2);
  return array;
};
reverseArrayInPlace(theArray);
console.log(theArray); // -> ["f", "e", "d", "c", "b", "a"]
1
drjorgepolanco

Voici quelques astuces que j'ai trouvées. Nous remercions Codemanx pour la solution originale de

array.sort(function() {
   return 1;
})

Dans TypeScript, cela peut être simplifié en une seule ligne

array.sort(() => 1)
var numbers = [1,4,9,13,16];

console.log(numbers.sort(() => 1));

Comme ce sera l'avenir de JavaScript, j'ai pensé partager cela.

Voici une autre astuce si votre tableau n'a que 2 éléments

array.Push(array.shift());
1
Richard Hamilton

J'ai trouvé un moyen simple de le faire avec .slice (). Reverse ()

var yourArray = ["first", "second", "third", "...", "etc"]
var reverseArray = yourArray.slice().reverse()

console.log(reverseArray)

Tu auras

["etc", "...", "third", "second", "first"]

Coller ce qui suit dans n'importe quelle console d'exécution javascript sur le navigateur ou node.js ferait un test de référence direct sur un grand nombre de nombres.

Dis 40 000 dans mon cas

var array = Array.from({ length: 40000 }, () =>
  Math.floor(Math.random() * 40000)
);
var beforeStart = Date.now();
var reversedArray = array.map((obj, index, passedArray) => {
  return passedArray[passedArray.length - index - 1];
});
console.log(reversedArray);
var afterCustom = Date.now();
console.log(array.reverse());
var afterNative = Date.now();
console.log(`custom took : ${afterCustom - beforeStart}ms`);
console.log(`native took : ${afterNative - afterCustom}ms`);

Vous pouvez simplement exécuter l'extrait directement pour voir comment la version personnalisée se comporte également.

0
Olaawo Oluwapelumi

Vous pouvez également utiliser reduceRight qui parcourra chaque valeur du tableau (de droite à gauche)

const myArray = [1, 2, 3, 4, 5]
const reversedArray = myArray.reduceRight((acc, curr) => [...acc, curr], [])
console.log(reversedArray) // [5, 4, 3, 2, 1]
0
Nick D