2010-11-29 17 views
5

digamos que tengo un archivo con números en cada línea:cómo números de grupo en diferentes cubos de rubí

0101 
1010 
1311 
0101 
1311 
431 
1010 
431 
420 

Al final me gustaría tener un hash con número de ocurrencias de cada número, en este caso :

{0101 => 2, 1010 => 2, 1311 => 2, 431 => 2, 420 => 1} 

¿cómo puedo lograrlo?

+1

Creo que he encontrado la misma pregunta con una redacción diferente :) [count elementos duplicados en la matriz de rubí] (http://stackoverflow.com/questions/569694/count-duplicate-elements-in-ruby-array) – Matchu

+0

:) eso es genial. Gracias. – josh

+0

FYI: Si se trata de Rails, puede usar Enumerable # group_by. Consulte http://api.rubyonrails.org/classes/Enumerable.html#method-i-group_by –

Respuesta

11

simple de una sola línea, teniendo en cuenta una serie items:

items.inject(Hash.new(0)) {|hash, item| hash[item] += 1; hash} 

Cómo funciona:

Hash.new(0) crea un nuevo hash, donde el acceso a las teclas definidas devuelve 0.

inject(foo) itera a través de una matriz con el bloque dado Para la primera iteración, pasa foo, y en iteraciones posteriores, pasa el valor de retorno de la última iteración.

Otra manera de escribir sería:

hash = Hash.new(0) 
items.each {|item| hash[item] += 1} 
+0

¿Cómo funciona eso? – josh

+1

Tiendo a hacerlo de la segunda manera que @Chuck mostró porque entonces no tengo que explicarlo, aunque prefiero usar 'inject()'. –

+0

El each_with_object ahora debería ser la solución preferida, más limpia, muy fácil de olvidar; picadillo. –

2
ID = -> x { x } # Why is the identity function not in the core lib? 

f = <<-HERE 
    0101 
    1010 
    1311 
    0101 
    1311 
    431 
    1010 
    431 
    420 
HERE 

Hash[f.lines.map(&:to_i).group_by(&ID).map {|n, ns| [n, ns.size] }] 
# { 101 => 2, 1010 => 2, 1311 => 2, 431 => 2, 420 => 1 } 

Simplemente grupo de los números por sí mismos utilizando Enumerable#group_by, que le da algo así como

{ 101 => [101, 101], 420 => [420] } 

y luego se Enumerable#map el valor las matrices a sus longitudes, es decir [101, 101] se convierte en 2. A continuación, simplemente conviértalo de nuevo a Hash usando Hash::[].

Sin embargo, si está dispuesto a utilizar una biblioteca de terceros, se vuelve aún más trivial, porque si utiliza una estructura de datos MultiSet, la respuesta se cae naturalmente. (A MultiSet es como un Set, con la excepción de que un artículo se puede añadir varias veces y la MultiSet será llevar la cuenta de cuántas veces se añadió un artículo   – que es exactamente lo que quiere.)

require 'multiset' # Google for it, it's so old that it isn't available as a Gem 

Multiset[*f.lines.map(&:to_i)] 
# => #<Multiset:#2 101, #2 1010, #2 1311, #2 431, #1 420> 

Sí, eso es eso.

Eso es lo mejor de usar la estructura de datos correcta: sus algoritmos se vuelven enormemente más simples. O, en este caso particular, el algoritmo solo desaparece.

He escrito más sobre el uso MultiSet s para la solución de este problema exacto en

+0

"¿Por qué la función de identidad no está en la biblioteca central?" - porque ninguno de nosotros ha sugerido que debería estar en el núcleo de lib? –

+0

Tal vez podríamos pedirle a 'tap' que acepte la ausencia de un bloque. http://stackoverflow.com/questions/6308470/ruby-method-that-returns-itself/6309488#6309488 –

4

Este es esencialmente el mismo que Chuck, pero cuando se está creando una matriz o hachís, 'each_with_object' hará es un poco más simple que 'inject', ya que no tiene que escribir la matriz final o hash en el bloque.

items.each_with_object(Hash.new(0)) {|item, hash| hash[item] += 1} 
Cuestiones relacionadas