2012-08-14 6 views
31

Je dispose d'un tableau de idsTrie un tableau selon les éléments d'un autre tableau

a1 = [1, 2, 3, 4, 5] 

et j'ai un autre tableau d'objets avec ids dans un ordre aléatoire

a2 = [(obj_with_id_5), (obj_with_id_2), (obj_with_id_1), (obj_with_id_3), (obj_with_id_4)] 

Maintenant, je dois trier a2 selon l'ordre des ids en a1. devrait devenir maintenant si a2:

[(obj_with_id_1), (id_2), (id_3), (id_4), (id_5)] 

a1 peut-être [3, 2, 5, 4, 1] ou dans un ordre quelconque mais a2 doit correspondre à l'ordre de ids dans a1.

Je aime ceci:

a1.each_with_index do |id, idx| 
    found_idx = a1.find_index { |c| c.id == id } 
    replace_elem = a2[found_idx] 
    a2[found_idx] = a2[idx] 
    a2[idx] = replace_elem 
end 

Mais cela pourrait encore fonctionner dans un temps O (n^2) si l'ordre des éléments de a2 est inverse exactement de a1. Quelqu'un peut-il me dire s'il vous plaît la façon la plus efficace de trier A2?

Répondre

23
hash_object = objects.each_with_object({}) do |obj, hash| 
    hash[obj.object_id] = obj 
end 

[1, 2, 3, 4, 5].map { |index| hash_object[index] } 
#=> array of objects in id's order 

Je crois que le temps d'exécution sera O (n)

+1

Je crois que ce serait O (n^2). le tri réel est O (n), mais l'étape de préparation le ferait n^2 – Jato

+1

Je ne suis pas d'accord, pour construire une table de hachage, il faut O (n), regardez ici http://en.wikipedia.org/wiki/ Hash_table – megas

+1

Oui, la construction de la table de hachage est O (n) temps. Et le tri est O (n) temps. Donc vous avez 2xO (n) ... hmmm ... ce serait moins que n^2. Je me suis trompé. bonne prise! – Jato

56

Je serai surpris si quelque chose est beaucoup plus rapide que la manière évidente:

a2.sort_by{|x| a1.index x.id} 
+0

en supposant que a1 est trié (et c'est à partir du problème stmt) et le conteneur que vous utilisez pour a1 peut tirer parti du fait que a1 est trié alors je suis d'accord que ce serait plus rapide que O (n^2). – Jato

+1

Aucun 1 étant trié n'est pas un avantage, je ne sais pas pourquoi vous penseriez cela. Cette façon de faire est rapide car elle est intégrée. Essayer de battre built_in sort_by semble une perte de temps pour moi. – pguardiario

+0

a1 étant trié est un avantage. Si elle est triée, l'opération d'index doit s'exécuter en O (log n) time (en supposant une recherche binaire) et si elle n'est pas triée, l'index s'exécutera en O (n) time. – Jato

14

J'aime la réponse acceptée , mais dans ActiveSupport, il y a index_by ce qui rend la création du hash initial encore plus facile. Voir Cleanest way to create a Hash from an Array

En fait, vous pouvez le faire en une seule ligne depuis Enumerable soutient index_by ainsi:

a2.index_by(&:id).values_at(*a1) 
+0

Cela ne fonctionne que si vous n'avez aucun doublon dans votre liste d'origine. L'index par remplacera tous les identifiants en double. Cela peut ou peut ne pas être un problème pour vous. – Josh

5

Inspirée par Eric Woodruff's Answer, je suis venu avec la solution vanille Ruby suivante:

a2.group_by(&:object_id).values_at(*a1).flatten(1) 

Documentation de méthode:

+0

J'aime mieux cette solution. C'est efficace (et je pense que c'est plus efficace que la solution @ pguardiario) et, surtout, permet à deux éléments de 'a2' d'avoir la même valeur pour" id ". La question n'indique pas que les identifiants sont uniques, mais certaines réponses, y compris celle qui est sélectionnée, dépendent de l'identité de l'identifiant. –

Questions connexes