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] }