tags:

views:

2003

answers:

4

I have an sorted array like this:

['FATAL <error title="Request timed out.">',
'FATAL <error title="Request timed out.">',
'FATAL <error title="There is insufficient system memory to run this query.">']

I would like to get something like this (does not have to be a hash):

[{:error => 'FATAL <error title="Request timed out.">', :count => 2}
{:error => 'FATAL <error title="There is insufficient system memory to run this query.">', :count => 1}]
+4  A: 

The following code prints what you asked for. I'll let you decide on how to actually use to generate the hash you are looking for:

# sample array
a=["aa","bb","cc","bb","bb","cc"]

# make the hash default to 0 so that += will work correctly
b = Hash.new(0)

# iterate over the array, counting duplicate entries
a.each do |v|
  b[v] += 1
end

b.each do |k, v|
  puts "#{k} appears #{v} times"
end

Note: I just noticed you said the array is already sorted. The above code does not require sorting. Using that property may produce faster code.

nimrodm
A good demonstration of the power of hash.
womble
I do not actually need to print it, just a hash did the trick. Thanks!
Željko Filipin
+1  A: 
a = [1,1,1,2,2,3]
a.uniq.inject([]){|r, i| r << { :error => i, :count => a.select{ |b| b == i }.size } }
=> [{:count=>3, :error=>1}, {:count=>2, :error=>2}, {:count=>1, :error=>3}]
Milan Novota
Oh, don't do that. You're reiterating through the whole array for each value!
glenn mcdonald
+5  A: 

You can do this very succinctly (one line) by using inject:

a = ['FATAL <error title="Request timed out.">',
      'FATAL <error title="Request timed out.">',
      'FATAL <error title="There is insufficient ...">']

b = a.inject(Hash.new(0)) {|h,i| h[i] += 1; h }

b.to_a.each {|error,count| puts "#{count}: #{error}" }

Will produce:

1: FATAL <error title="There is insufficient ...">
2: FATAL <error title="Request timed out.">

Cheers, V.

vladr
A: 

Simple implementation:

(errors_hash = {}).default = 0
array_of_errors.each { |error| errors_hash[error] += 1 }
Evan Senter