tags:

views:

185

answers:

5

I know of the ||= operator, but don't think it'll help me here...trying to create an array that counts the number of "types" among an array of objects.

array.each do |c|
  newarray[c.type] = newarray[c.type] ? newarray[c.type]+1 ? 0
end

Is there a more graceful way to do this?

+2  A: 

||= does help:

types = {}
array.each do |c|
    types[c.class] ||= 0
    types[c.class] += 1
end
yjerem
That'll set it to 1 the first time through... newarray[c.type] ||= -1 newarray[c.type] += 1
Eric Nicholson
Yes, isn't that what we want? For example, if array is [1] then types will become {Fixnum=>1}. And we have 1 Fixnum.
yjerem
We want it to be set to 0 at the end of the loop, not 1, if !types[c.class].
Chuck
+3  A: 
array.each do |c|
  newarray[c.type] = 1 + (newarray[c.type] || -1)
end

Alternatively

array.each do |c|
  newarray[c.type] ||= -1
  newarray[c.type] += 1
end
Josh Matthews
I like your second one. The only drawback is that it's the sort of thing that someone coming along later may take for being a mistake. If using it in production, I'd add a quick comment explaining why I'm doing it that way.
Pesto
I like the second way. It's two lines and potentially two assignments, but more readable than complex conditionals or whatever shoved on one line.
Chuck
+3  A: 

Use the Array#fetch method for which you can provide a default value if the index doesn't exist:

array.each do |c|
  newarray[c.type] = newarray.fetch(c.type, -1) + 1
end
glenn jackman
+4  A: 
types = Hash.new(-1) # It feels like this should be 0, but to be
                     # equivalent to your example it needs to be -1
array.each do |c|
  types[c.type] += 1
end
sepp2k
A: 

Your variable newarray is named oddly, since in Ruby and most other languages, arrays are indexed by integers, not random Objects like Class. It's more likely this is a Hash.

Also, you should be using c.class, instead of c.type, which is deprecated.

Finally, since you're creating a Hash, you can use inject like so:

newarray = array.inject( {} ) do |h,c|
  h[c.class] = h.key?(c.class) ? h[c.class]+1 : 0
  h
end

Or, for a one-liner:

newarray = array.inject( {} ) { |h,c| h[c.class] = h.key?(c.class) ? h[c.class]+1 : 0 ; h }

As you can see, this gives the desired results:

irb(main):001:0> array = [1, {}, 42, [], Object.new(), [1, 2, 3]]
=> [1, {}, 42, [], #<Object:0x287030>, [1, 2, 3]]
irb(main):002:0> newarray = array.inject( {} ) { |h,c| h[c.class] = h.key?(c.class) ? h[c.class]+1 : 0 ; h }
=> {Object=>0, Hash=>0, Array=>1, Fixnum=>1}
Rudd Zwolinski