Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I create an array in Ruby with default values?

Tags:

arrays

ruby

Perl is pretty nice about default values:

: jmglov@laurana; perl -e '@foo; printf "%d\n", $foo[123]' 0 : jmglov@laurana; perl -e '%foo; printf "%d\n", $foo{bar}' 0 

Ruby can do the same, at least for hashes:

>> foo = Hash.new(0) => {} >> foo[:bar] => 0 

But the same seemingly does not work for arrays:

>> foo = Array.new(0) => [] >> foo[123] => nil >> foo[124] = 0 => 0 >> foo[456] = 0 => 0 >> foo[455,456] => [nil, 0] 

Is it possible to supply a default value for arrays, so when they are auto-extended, they're filled with 0 instead of nil?

Of course I can work around this, but at a cost to expressiveness:

>> foo[457,458] = 890, 321 => [890, 321] >> foo[456] += 789 NoMethodError: You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occurred while evaluating nil.+ >> foo.inject(0) {|sum, i| sum += (i || 0) } => 1211 >> foo.inject(:+) NoMethodError: You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occurred while evaluating nil.+ 

Update 1: One of my colleagues pointed out that I can use #compact to solve the #inject issue, and #to_i to solve the standard element-at-index issue:

>> foo.include? nil => true >> foo.compact.inject(:+) => 1211 >> foo[456,457] => [0, 890, 321] >> foo[455..457] => [nil, 0, 890] >> foo[455..457].map(&:to_i) => [0, 0, 890] 

Update 2: Thanks to Andrew Grimm for a solution to the += issue:

>> foo = [] => [] >> def foo.[](i) >>   fetch(i) {0} >> end => nil >> foo[4] => 0 >> foo => [] >> foo[4] += 123 => 123 >> foo => [nil, nil, nil, nil, 123] 

Update 3: this is starting to look like whack-a-mole!

>> foo => [nil, nil, nil, nil, 123] >> foo[-2..-1] TypeError: can't convert Range into Integer 

But we can deal with that:

>> def foo.[](index) >>   if index.is_a? Range >>     index.map {|i| self[i] } >>   else ?>     fetch(index) { 0 }  # default to 0 if no element at index; will not cause auto-extension of array >>   end >> end => nil >> foo => [nil, nil, nil, nil, 123] >> foo[-2..-1] => [nil, 123] 

I now have to admit (sheepishly) that I'll subclass Array to avoid cluttering my code:

class MyClass   class ArrayWithDefault < Array     def [](index)       if index.is_a? Range         index.map {|i| self[i] }       else         fetch(index) { 0 }  # default to 0 if no element at index; will not cause auto-extension of array       end     end   end end 

Thanks for all the creative solutions. TIMTOWTDI indeed!

like image 810
Josh Glover Avatar asked Mar 16 '11 11:03

Josh Glover


People also ask

How do you create an array with default value?

Using default values in initialization of array For double or float , the default value is 0.0 , and the default value is null for string. Type[] arr = new Type[capacity]; For example, the following code creates a primitive integer array of size 5 . The array will be auto-initialized with a default value of 0 .

How do you create an array of objects in Ruby?

You can create an array by separating values by commas and enclosing your list with square brackets. In Ruby, arrays always keep their order unless you do something to change the order. They are zero-based, so the array index starts at 0.


1 Answers

Not auto extended, but initialized to the specified length with a default value:

>> Array.new(123, 0)   => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 
like image 159
ludde Avatar answered Oct 07 '22 12:10

ludde