2010-11-21 3 views
1

Je cherche un moyen (en JavaScript) pour collecter un ensemble d'objets dans plusieurs tableaux, où chaque tableau contient un certain type d'objet, et les tableaux sont stockés en tant que valeurs dans un tableau associatif, les clés étant les les types. Par exemple:Qu'est-ce qu'un bon modèle JavaScript pour catégoriser les choses en types?

Entrée:

[<apple>, <cat>, <pear>, <mercedes>, <dog>, <ford>, <orange>] 

Sortie:

{ 
    'fruit': [<apple>, <pear>, <orange>], 
    'animal': [<cat>, <dog>], 
    'car': [<mercedes>, <ford>] 
} 

En ruby, vous pourriez faire ce qui suit:

things_by_type = {} 
things.each do |thing| 
    (things_by_type[thing.type] ||= []) << thing 
end 

qui est agréable et concis.

Qu'est-ce qu'un bon motif pour faire la même chose en JavaScript qui est concis et efficace? Je pouvais faire quelque chose comme ça, mais ce n'est pas aussi agréable:

var thing, things_by_type = {}; 
for (var i = 0; i < things.length; i++) { 
    thing = things[i]; 
    if(things_by_type[thing.type]) { 
    things_by_type[thing.type].push(thing); 
    } else { 
    things_by_type[thing.type] = [thing]; 
    } 
} 
+0

J'ai toujours pensé Javascript a besoin d'un '' || = opérateur. Dommage que vous ayez à taper tout deux fois. – galambalazs

Répondre

1

Je ne suis pas sûr que ce soit un bon modèle, mais il est similaire à votre échantillon de rubis:

var things_by_type = {}; 
for (var i in things) { 
    var thing = things[i]; 
    (things_by_type[thing.type] || (things_by_type[thing.type] = [])).push(thing); 
} 

Et si vous pouvez supposer Javascript 1.6:

var things_by_type = {}; 
things.forEach(function(thing) { 
    (things_by_type[thing.type] || (things_by_type[thing.type] = [])).push(thing); 
}) 
+2

Pour ceux qui ne le savent pas, vous pouvez définir 'Array.prototype.forEach' si vous n'utilisez pas JS1.6 pour fonctionner exactement comme si vous l'aviez fait. –

+0

Voir https://developer.mozilla.org/fr/JavaScript/Reference/Global_Objects/Array/forEach#Compatibility pour une implémentation forEach –

+0

** Ne pas ** utiliser 'for..in' pour les tableaux. C'est une très mauvaise pratique. – galambalazs

0

presque le même code, mais fonctionne un peu différent, vous pouvez utiliser la fonction de jeu de fantaisie plus facile et il sépare la logique:

var a = {set:function(type,thing){ 
    if (this[type]) { 
    this[type].push(thing); 
    } else { 
    this[type] = [thing]; 
    } 
}}; 

a.set('a',0); 
a.set('b',1); 
a.set('a',2); 
1

En ruby, vous pouvez effectuer les opérations suivantes:

things_by_type = {} 
things.each do |thing| 
    (things_by_type[thing.type] ||= []) << thing 
end 

qui est agréable et concis.

En fait, vous pouvez rendre cela encore plus agréable.

Tout d'abord, Hash.new prend un argument de bloc qui sera appelé chaque fois qu'une clé non existante est référencée. Vous pouvez l'utiliser pour créer cette clé. De cette façon, vous vous débarrassez de la logique conditionnelle à l'intérieur du bloc.

things_by_type = Hash.new {|h, k| h[k] = [] } 
things.each do |thing| 
    things_by_type[thing.type] << thing 
end 

En second lieu, ce que vous avez ici est appelé fold ou reduce: vous « pliage » ou « réduire » une collection (tableau d'objets) en une seule valeur (le hachage, qui se prêter à confusion aussi être une collection, mais n'en est pas moins une valeur unique).

Vous pouvez généralement facilement repérer ce modèle en recherchant les endroits où vous initialisez une variable, puis bouclez une collection et manipulez cette variable à chaque itération de la boucle.

Ruby a pliage intégré, via la méthode Enumerable#reduce:

things.reduce(Hash.new {|h, k| h[k] = [] }) do |h, thing| 
    h.tap { h[thing.type] << thing } 
end 

Mais ce que vous êtes vraiment faire, est groupant le tableau par l'attribut type de ses éléments, qui est également construit en Ruby comme Enumerable#group_by:

things.group_by {|thing| thing.type } 

qui peut encore être simplifiée en utilisant Symbol#to_proc à

things.group_by(&:type) 

Malheureusement, ECMAScript n'a pas groupBy, ni les valeurs par défaut pour les propriétés non-existantes, mais il ne ont Array.prototype.reduce:

things.reduce(function (acc, thing) { 
    (acc[thing.type] || (acc[thing.type] = [thing])).push(thing); 
    return acc; 
}, {}); 
+0

Merci Jorg. Vos suggestions pour rendre la version Ruby plus efficace sont impressionnantes. –

Questions connexes