1

I would like to be able to do this:

my_array = Array.new
my_array[12] += 1

In other words, somehow upon trying to access entry 12, finding it uninitialized, it is initialized to zero so I can add one to it. Array.new has a default: parameter, but that comes into play when you initialize the array with a known number of slots. Other than writing my own class, is there a ruby-ish way of doing this?

4
  • Can you use my_array[12] ||= 1 ? This will initialize if array[12] is nil Commented Jan 27, 2017 at 17:21
  • Good lead but then I still have to increment it if was not nil Commented Jan 27, 2017 at 17:25
  • What's the size of the array? Is 11 the last element, and 12 the one just after that one? (If it's not the last you should do something with the ones inbetween) Commented Jan 27, 2017 at 17:26
  • Whole point is that the array length is not known Commented Jan 27, 2017 at 17:31

5 Answers 5

4

No need to create a new class :

my_hash = Hash.new(0)
my_hash[12] += 1
p my_hash
#=> {12=>1}

For many cases, hashes and arrays can be used interchangeably. An array with an arbitrary number of elements and a default value sounds like a hash to me ;)

Just to make it clear : Hash and Array aren't equivalent. There will be cases where using a hash instead of an array will be completely wrong.

Sign up to request clarification or add additional context in comments.

6 Comments

I knew that Hash had that but it never occurred to me to use it instead of an array!
Unfortunately, this is plain wrong. Try a = []; a[12] = 1; puts a and you’ll see what I am saying.
@mudasobwa : without knowing more about the OP needs, it's too early to tell IMHO. My solution could be plain wrong, it could also work just fine. With your example, what exactly would be the problem, with which use case?
There are side effects of using Array, which are absent in case of using Hash. Side effects are the most hard-to-find evil-to-debug etc part of the code. This should be at least understood, taken into account and mentioned :)
“what exactly would be the problem”—setting a[12] in the array results in a.count #⇒ 13. In hash count will return guess what?
|
3

Something like:

a[12] = (a[12] ||= 0) + 1

3 Comments

redundant assignment here, a[12] = (a[12] || 0) + 1 works too.
@Coolness : Please don't
I like this best because its the most direct representation I was looking for, but the idea with a Hash is also excellent!
1

Making use of nil.to_i == 0

my_array = Array.new
my_array[12] = my_array[12].to_i + 1

Comments

0

Note, that unlike other solutions here so far, this one works for any arbitrary initial value.

my_array = Array.new.extend(Module.new {
  def [] idx
    super || 0
  end
})
my_array[12] += 1
#⇒ 1

2 Comments

This should be how new works, but sadly it's not the case. Maybe subclassing Array into ArrayWithDefault is a better plan than a dynamic mixin. This could get punishingly expensive if a lot of these are created since each instance has its own pet module.
@tadman yes, this is true; I have just measured it, the penalty is the pet module is 20× slower. My intent was to show the approach, optimization is on the reader. In any case, the “proper” answer here was lazy initialization of the array member.
0

This is not possible with the stock Array::new method.

https://docs.ruby-lang.org/en/2.0.0/Array.html#method-c-new

You will either need to monkey patch Array class, or monkey patch nil class. And they are not recommended.

If you have a specific use case, I would create a new wrapper class around Array

class MyArray < Array
  def [](i)
    super(i) ? super(i) : self[i] = 0
  end
end

arr = MyArray.new
arr[12] += 1 # => 1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.