web-dev-qa-db-fra.com

tableau javascript associatif ET indexé?

un tableau dans JS peut-il être associatif ET indexé?

36
puffpio

Les tableaux associatifs n'existent pas en Javascript. Vous pouvez utiliser des littéraux d'objet, qui ressemblent à des tableaux associatifs, mais ils ont des propriétés non ordonnées. Les tableaux Javascript standard sont basés sur des index entiers et ne peuvent pas être associatifs.

Par exemple, avec cet objet:

var params = {
    foo: 1,
    bar: 0,
    other: 2
};

Vous pouvez accéder aux propriétés à partir de l'objet, par exemple:

params["foo"];

Et vous pouvez également parcourir l'objet en utilisant l'instruction for...in:

for(var v in params) {
    //v is equal to the currently iterated property
}

Cependant, il n'y a pas de règle stricte sur l'ordre des itérations de propriétés: deux itérations de votre littéral d'objet peuvent renvoyer les propriétés dans des ordres différents.

27
Alex Rozanski

Après avoir lu la définition de Wikipedia du tableau associatif , je vais rompre avec la tradition JavaScript traditionnelle et dire: "Oui, JavaScript a des tableaux associatifs." Avec les tableaux JavaScript, vous pouvez ajouter, réaffecter, supprimer et rechercher des valeurs à l'aide de leurs clés (et les clés peuvent être des chaînes entre guillemets), ce que Wikipedia dit que les tableaux associatifs devraient pouvoir faire.

Cependant, vous semblez demander quelque chose de différent - que vous puissiez rechercher la même valeur par index ou par clé. Ce n'est pas une exigence des tableaux associatifs (voir l'article de Wikipedia.) Les tableaux associatifs n'ont pas à vous donner la possibilité d'obtenir une valeur par index.

Les tableaux JavaScript sont très proches des objets JavaScript.

  arr=[];
  arr[0]="zero";
  arr[1]="one";
  arr[2]="two";
  arr["fancy"]="what?";

Oui, c'est un tableau, et oui, vous pouvez vous en tirer avec des index non numériques. (Si vous êtes curieux, après tout cela, arr.length est 3.)

Dans la plupart des cas, je pense que vous devriez vous en tenir aux index numériques lorsque vous utilisez des tableaux. C’est ce que la plupart des programmeurs attendent, je pense.

Le lien est à mon blog sur le sujet.

22
Nosredna

Les objets JS natifs n'acceptent que les chaînes en tant que noms de propriétés, ce qui est vrai même pour les index de tableaux numériques ; Les tableaux diffèrent des objets Vanilla uniquement dans la mesure où la plupart des implémentations JS stockent différemment les propriétés indexées numériquement (c'est-à-dire dans un tableau réel tant qu'elles sont denses) et leur réglage déclenchera des opérations supplémentaires (par exemple, l'ajustement de la propriété length).

Si vous recherchez une carte qui accepte des clés arbitraires, vous devrez utiliser une implémentation non-native . Le script est conçu pour une itération rapide et non pour un accès aléatoire à l'aide d'index numériques; il ne correspond donc peut-être pas à ce que vous recherchez.

Une implémentation de base d'une carte qui ferait ce que vous demandez pourrait ressembler à ceci:

function Map() {
    this.length = 0;
    this.store = {};
}

Map.prototype.get = function(key) {
    return this.store.hasOwnProperty(key) ?
        this.store[key] : undefined;
};

Map.prototype.put = function(key, value, index) {
    if(arguments.length < 3) {
        if(this.store.hasOwnProperty(key)) {
            this.store[key].value = value;
            return this;
        }

        index = this.length;
    }
    else if(index >>> 0 !== index || index >= 0xffffffff)
        throw new Error('illegal index argument');

    if(index >= this.length)
        this.length = index + 1;

    this[index] = this.store[key] =
        { index : index, key : key, value : value };

    return this;
};

L'argument index de put() est facultatif.

Vous pouvez accéder aux valeurs d’une mappe map par clé ou par index via

map.get('key').value
map[2].value
9
Christoph

L'ordre dans lequel les objets apparaissent dans un tableau javascript associatif n'est pas défini et sera différent selon les implémentations. Pour cette raison, vous ne pouvez pas vraiment compter sur une clé associative donnée pour être toujours au même index.

MODIFIER: 

comme le souligne Perspx, il n’existe pas vraiment de tableaux associatifs en javascript. L'instruction foo["bar"] est simplement un sucre syntaxique pour foo.bar

Si vous faites confiance au navigateur pour maintenir l'ordre des éléments dans un objet, vous pouvez écrire une fonction

function valueForIndex(obj, index) {

    var i = 0;
    for (var key in obj) {

        if (i++ == index)
            return obj[key];
    }
}
6
Matt Bridges
var myArray = Array();
myArray["first"] = "Object1";
myArray["second"] = "Object2";
myArray["third"] = "Object3";

Object.keys(myArray);              // returns ["first", "second", "third"]
Object.keys(myArray).length;       // returns 3

si vous voulez le premier élément, vous pouvez l'utiliser comme suit:

myArray[Object.keys(myArray)[0]];  // returns "Object1"
5
Tom Prats

Je suis venu ici pour savoir si c'était une mauvaise pratique ou non, mais j'ai constaté que beaucoup de personnes semblaient ne pas comprendre la question.

Je souhaitais avoir une structure de données ordonnée mais pouvant être indexée par clé, de sorte qu'elle ne nécessite pas d'itération à chaque recherche. 

Sur le plan pratique, cela est assez simple, mais je n’ai toujours rien lu sur la question de savoir si c’est une pratique terrible ou non.

var roygbiv = [];
var colour = { key : "red", hex : "#FF0000" };
roygbiv.Push(colour);
roygbiv[colour.key] = colour;
...
console.log("Hex colours of the Rainbow in order:");
for (var i = 0; i < roygbiv.length; i++) {
    console.log(roygbiv[i].key + " is " + roygbiv[i].hex);
}

// input = "red";
console.log("Hex code of input colour:");
console.log(roygbiv[input].hex);

L'important est de ne jamais changer la valeur de array [index] ou array [key] directement une fois que l'objet est configuré ou les valeurs ne correspondront plus. Si le tableau contient des objets, vous pouvez en modifier les propriétés et accéder aux propriétés modifiées par l’une ou l’autre des méthodes.

1
Joel Roberts
var stuff = [];
stuff[0] = "foo";
stuff.bar = stuff[0]; // stuff.bar can be stuff["bar"] if you prefer
var key = "bar";
alert(stuff[0] + ", " + stuff[key]); // shows "foo, foo"
1
NickFitz

Bien que je sois d’accord avec les réponses données, vous pouvez réellement accomplir ce que vous dites avec des accesseurs et des passeurs. Par exemple:

var a = [1];
//This makes a["blah"] refer to a[0]
a.__defineGetter__("blah", function(){return this[0]});
//This makes a["blah"] = 5 actually store 5 into a[0]
a.__defineSetter__("blah", function(val){ this[0] = val});

alert(a["blah"]); // emits 1
a["blah"] = 5;
alert(a[0]); // emits 5

Est-ce ce que vous recherchez? Je pense qu'il y a une façon plus moderne de faire des accesseurs et des setters, mais je ne m'en souviens plus. 

0
Ryan

La marée a changé sur celui-ci. Maintenant, vous pouvez le faire ... et PLUS! En utilisant Harmony Proxies, vous pouvez certainement résoudre ce problème de nombreuses manières. 

Vous devrez vérifier que vos environnements ciblés prennent en charge cela avec peut-être un peu d'aide de la commande harmony-reflect .

Sur le réseau de développeurs Mozilla, il existe un très bon exemple d'utilisation d'un proxy pour trouver un objet d'élément de tableau par sa propriété , ce qui le résume assez bien. 

Voici ma version:

  var players = new Proxy(
  [{
    name: 'monkey',
    score: 50
  }, {
    name: 'giraffe',
    score: 100
  }, {
    name: 'pelican',
    score: 150
  }], {
    get: function(obj, prop) {
      if (prop in obj) {
        // default behavior
        return obj[prop];
      }
      if (typeof prop == 'string') {

        if (prop == 'rank') {
          return obj.sort(function(a, b) {
            return a.score > b.score ? -1 : 1;
          });
        }

        if (prop == 'revrank') {
          return obj.sort(function(a, b) {
            return a.score < b.score ? -1 : 1;
          });
        }

        var winner;
        var score = 0;
        for (var i = 0; i < obj.length; i++) {
          var player = obj[i];
          if (player.name == prop) {
            return player;
          } else if (player.score > score) {
            score = player.score;
            winner = player;
          }
        }

        if (prop == 'winner') {
          return winner;
        }
        return;
      }

    }
  });

  console.log(players[0]); // { name: 'monkey', score: 50 }
  console.log(players['monkey']); // { name: 'monkey', score: 50 }
  console.log(players['zebra']); // undefined
  console.log(players.rank); // [ { name: 'pelican', score: 150 },{ name: 'giraffe', score: 100 }, { name: 'monkey', score: 50 } ]
  console.log(players.revrank); // [ { name: 'monkey', score: 50 },{ name: 'giraffe', score: 100 },{ name: 'pelican', score: 150 } ]
  console.log(players.winner); // { name: 'pelican', score: 150 }
0
Quickredfox