# Array Count

I have an array that contains the following:

114,151,157,151,10,12,14,151,157,10

I need to count how many instances of each item show up in this string.
In this case:

114 shows up 1 time
151 shows up 3 times
157 shows up 2 times
10 shows up 2 times
12 shows up 1 time
14 shows up 1 time

From there I need to match it back up so I can define this in an orderly
fashion. Ala:

counted151 = "3",
counted157 = "2",
counted10 = "2",
counted114 = "1",
counted14 = "1",
counted12 = "1",

If you haven't guessed by now this only my 12th hour using RoR, so the
code that I have so far simply creates the array and does a .length
method for a count variable. I have a feeling this job requires
array.map and I've been playing around with that method but I still cant
be certain as to how to implement it. Any tips or ideas would be greatly
appreciated. In the meantime I'll keep at it.

Ricky Kilmer wrote:

I have an array that contains the following:

114,151,157,151,10,12,14,151,157,10

I need to count how many instances of each item show up in this string.
In this case:

114 shows up 1 time
151 shows up 3 times

I bet someone can find an even leaner way than this:

\$ irb
> array = [114,151,157,151,10,12,14,151,157,10]
> counts = {}
> array.each{|cell| counts[cell] = 0 }
> array.each{|cell| counts[cell] += 1 }
> p counts

{12=>1, 151=>3, 14=>1, 157=>2, 114=>1, 10=>2}

Ruby is really great for tiny expressions that do a lot. (And I used 'cell' because your data structure is a histogram...)

I bet someone can find an even leaner way than this:

Now you've said that I've got to!

\$ irb
> array = [114,151,157,151,10,12,14,151,157,10]
> counts = {}
> array.each{|cell| counts[cell] = 0 }
> array.each{|cell| counts[cell] += 1 }
> p counts

array.inject(Hash.new(0)) {|memo, value| memo[value]+=1; memo}
Using a default value for the hash means you only need to iterate over
it once.

Fred

Phlip wrote:

Ricky Kilmer wrote:

I have an array that contains the following:

114,151,157,151,10,12,14,151,157,10

I need to count how many instances of each item show up in this string.
In this case:

114 shows up 1 time
151 shows up 3 times

I bet someone can find an even leaner way than this:

\$ irb
> array = [114,151,157,151,10,12,14,151,157,10]
> counts = {}
> array.each{|cell| counts[cell] = 0 }
> array.each{|cell| counts[cell] += 1 }
> p counts

{12=>1, 151=>3, 14=>1, 157=>2, 114=>1, 10=>2}

Ruby is really great for tiny expressions that do a lot. (And I used
'cell'
because your data structure is a histogram...)

--
Phlip

That did the trick. And I was able to sort it nicely with:

p counts.sort {|a,b| -1*(a[1]<=>b[1]) }

Frederick Cheung wrote:

I bet someone can find an even leaner way than this:

Now you've said that I've got to!

\$ irb
�> array = [114,151,157,151,10,12,14,151,157,10]
�> counts = {}
�> array.each{|cell| counts[cell] = 0 }
�> array.each{|cell| counts[cell] += 1 �}
�> p counts

array.inject(Hash.new(0)) {|memo, value| memo[value]+=1; memo}
Using a default value for the hash means you only need to iterate over
it once.

Fred

That didn't seem to work for me and I'm not sure why, probably because
to be honest I don't know what your line actually does. What does the
semicolon do?:

array = [114,151,157,151,10,12,14,151,157,10]
array.inject(Hash.new(0)) {|memo, value| memo[value]+=1; memo}
puts hash

outputs an unknown integer. More likely than not, I am not executing it
correctly.

Thanks for the tips guys. You should have seen the mess I was trying to
build with .map and counts lol. I cant wait to get as good as you.

array = [114,151,157,151,10,12,14,151,157,10]
array.inject(Hash.new(0)) {|memo, value| memo[value]+=1; memo}
puts hash

outputs an unknown integer. More likely than not, I am not executing
it
correctly.

inject returns the value you want, so
result = array.inject(Hash.new(0)) {|memo, value| memo[value]+=1; memo}
puts result
would do what you want

What inject does is take an initial value, and pass that and each
element of the collection to the block, and then updates the initial
value with the result of the block. So for example, you can rewrite
the sum function as

array.inject(0) {|memo, value| memo = value}

The semicolon is just to keep everything on the same line, because i
need the result of the block to be memo.
Fred

That did the trick. And I was able to sort it nicely with:

p counts.sort {|a,b| -1*(a[1]<=>b[1]) }

Just as a note here, you could sort it as:

p counts.sort {|a,b| b[1]<=>a[1] }

Which might be a little more obvious to someone reading the code. (I was confused by the -1*( ) on first glance )

That didn't seem to work for me and I'm not sure why, probably because
to be honest I don't know what your line actually does. What does the
semicolon do?:

array = [114,151,157,151,10,12,14,151,157,10]
array.inject(Hash.new(0)) {|memo, value| memo[value]+=1; memo}
puts hash

outputs an unknown integer. More likely than not, I am not executing it
correctly.

The semi colon is a statement seperator. It lets you have multiple ruby statements on a line. In this case, it's there so that the second 'line' (memo) returns the hash to the block so that it can be used again.

Inject works by iterating over the collection (array), giving the block the current result (memo) and the current item (value) from the collection. At the end of each invocation of the block, result is set to the return value of the block, which is the return value of the last statement evaluated. A variable on it's own, like memo, returns the variable itself, so we construct that over the course of the iterations, and then return it at the end.

The block works for me, though the hash which is constructed is the return value, so line should be

counts = array.inject(Hash.new(0)) {|memo, value| memo[value]+=1; memo}

p counts

hth,
Jon

@Rick I can name that tune in two lines:

array = [114,151,157,151,10,12,14,151,157,10]
array.group_by{|val| val}.each{|val, occurs| puts "#{val} occurs
#{occurs.size} times"}

Jonathan Stott wrote:

That did the trick. And I was able to sort it nicely with:

p counts.sort {|a,b| -1*(a[1]<=>b[1]) }

Just as a note here, you could sort it as:

p counts.sort {|a,b| b[1]<=>a[1] }

Which might be a little more obvious to someone reading the code. (I
was confused by the -1*( ) on first glance )

correctly.

The semi colon is a statement seperator. It lets you have multiple ruby
statements on a line. In this case, it's there so that the second
'line' (memo) returns the hash to the block so that it can be used
again.

Inject works by iterating over the collection (array), giving the block
the current result (memo) and the current item (value) from the
collection. At the end of each invocation of the block, result is set
to the return value of the block, which is the return value of the last
statement evaluated. A variable on it's own, like memo, returns the
variable itself, so we construct that over the course of the iterations,
and then return it at the end.

The block works for me, though the hash which is constructed is the
return value, so line should be

counts = array.inject(Hash.new(0)) {|memo, value| memo[value]+=1; memo}

p counts

hth,
Jon

Ahh. Not only do I see where I went wrong on executing it, but I
understand how it works.

You are right about sorting, it can be ascending values

.sort {|a,b| a[1]<=>b[1] }

or descending values

.sort {|a,b| -1*(a[1]<=>b[1]) }

Frederick Cheung wrote:

Phlip wrote:

I bet someone can find an even leaner way than this:

Now you've said that I've got to!

Thanks! Even though raw Ruby is best served on news:comp.lang.ruby , not here!

(-:

You are right about sorting, it can be ascending values

.sort {|a,b| a[1]<=>b[1] }

or descending values

.sort {|a,b| -1*(a[1]<=>b[1]) }

You can also sort descending via:

.sort {|a,b| b[1]<=>a[1] }

(note the swapped 'a' and 'b') as I said in the previous email. (Although multiplying the result by -1 also works, yes)

Jon

Jonathan Stott wrote:

.sort {|a,b| b[1]<=>a[1] }

(note the swapped 'a' and 'b') as I said in the previous email.

Note also that many sorts with the spaceship operator <=> can simplify with sort_by:

.sort_by{|a| -a[1] }