2012-08-14 16 views
31

tengo una tabla de idordenar una matriz de acuerdo con los elementos de otra matriz

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

y tengo otro conjunto de objetos con ids en orden aleatorio

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

Ahora necesito para ordenar a2 según el orden de los identificadores en a1. Así a2 ahora debe convertirse en:

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

a1 podría ser [3, 2, 5, 4, 1] o en cualquier orden pero a2 debe corresponder al orden de ids en a1.

hago así:

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 

Pero esto todavía podría funcionar en un tiempo O (n^2) si el orden de los elementos de A2 es exactamente inversa de a1. ¿Puede alguien decirme la forma más eficiente de ordenar a2?

Respuesta

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 

creo que el tiempo de ejecución será O (n)

+1

creo que esto sería O (n^2). el tipo real es O (n), pero el paso de preparación lo haría n^2 – Jato

+1

No estoy de acuerdo, para construir la tabla hash se requiere O (n), mira aquí http://en.wikipedia.org/wiki/ Hash_table – megas

+1

Sí, construir la tabla hash es O (n) tiempo. Y el género es O (n) tiempo. Entonces tienes 2xO (n) ... hmmm ... eso sería menos que n^2. Estoy corregido. ¡buena atrapada! – Jato

56

Me sorprendería si algo es mucho más rápido que la manera obvia:

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

suponiendo que a1 está ordenado (y es del problema stmt) y el contenedor que está utilizando para a1 puede aprovechar el hecho de que a1 está ordenado, entonces acepto que sería más rápido que O (n^2). – Jato

+1

No a1 ser ordenado no es una ventaja, no estoy seguro de por qué pensaría eso. De esta manera es rápido porque está incorporado. Tratar de vencer al sort_by incorporado me parece una pérdida de tiempo. – pguardiario

+0

a1 siendo ordenado es una ventaja. Si está ordenado, entonces la operación de índice debe ejecutarse en el tiempo O (log n) (asumiendo la búsqueda binaria) y si no está ordenada, el índice se ejecutará en el tiempo O (n). – Jato

14

me gusta la respuesta aceptada , pero en ActiveSupport hay index_by que hace que crear el hash inicial sea aún más fácil. Ver Cleanest way to create a Hash from an Array

De hecho, usted puede hacer esto en una sola línea desde Enumerable apoya index_by así:

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

Esto solo funciona si no tiene ningún duplicado en su lista original. Index by will sobrescribirá cualquier identificación duplicada. Esto puede o no ser un problema para usted. – Josh

5

Inspirado por Eric Woodruff's Answer, me ocurrió con la siguiente solución de vainilla Ruby:

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

Documentación de método:

+0

Me gusta más esta solución. Es eficiente (y sospecho que es más eficiente que la solución de @ pguardiario) y, lo que es más importante, permite que dos elementos de 'a2' tengan el mismo valor para" id ". La pregunta no establece que los ID sean únicos, sin embargo, algunas respuestas, incluida la que se selecciona, dependen de que el ID sea único. –

Cuestiones relacionadas