How can I create a dynamic array, whose default element will empty hash?

Hi,

If I write array = Array.new(4) { {} } , it will create an array of
empty hashes of size 4. Now if I do array[3], array[2] etc, I would get
{}. But now, if I do array[4] or array[5], I will get nil.
Reason is clear. But I am looking for a way to get an empty hash ( for
first time ), for any index access from array ?

I mean

array[7] # => {}
array[11] # => {} # although I created array of size 4.

Regards,
Arup R.

Only way I can think of is if you subclass Array, and override #[]?
You’d then
have to answer all the questions of how that interface would work in
your case.
For example:

ruby -e ‘a = []; a[4] = true; puts a.inspect’ # => [nil, nil, nil, nil,
true]

Your array is now length 5.

And what about slices?

I wouldn’t suggest this route however, but it does answer the question.
I think
I would add another method onto that new class, so it’s more clear:

def item_or_hash_by_index(index)
self[index].nil? ? self[index] = {} : self[index]
end

I was actually looking for Hash#default=
(Class: Hash (Ruby 2.1.1)). Why
Array class don’t have any such method, like Hash class has ?
Hash#default= is very useful although.

Regards,
Arup R.
On Thursday, 15 May 2014 6:18 PM, Craig M. [email protected]
wrote:

Only way I can think of is if you subclass Array, and override #[]?
You’d then
have to answer all the questions of how that interface would work in
your case.
For example:

ruby -e ‘a = []; a[4] = true; puts a.inspect’ # => [nil, nil, nil, nil,
true]

Your array is now length 5.

And what about slices?

I wouldn’t suggest this route however, but it does answer the question.
I think
I would add another method onto that new class, so it’s more clear:

def item_or_hash_by_index(index)
self[index].nil? ? self[index] = {} : self[index]
end

Hi Arup,

Look,

a = Array.new

a

=> [] # Empty

a[5] = “five”

a

=> [nil, nil, nil, nil, nil, “five”]

Look, a[0] is “really” nil by now!

If we use Hash

h = Hash.new
h[5] = “five”
h

=> {5=>“five”}

There’s no nil on key 0 or 1 or 2…

h[1]

=> nil

h.fetch(1)

It raises an exception!!!

KeyError: key not found: 1
from (irb):11:in fetch' from (irb):11 from /Users/abinoam/.rvm/rubies/ruby-2.1.0/bin/irb:11:in

h.fetch(1) { “My Defaul Value” }

=> “My Default Value”

If you really need to use Array you can simply use an “or” like

my_var = a[1] || “My Default Value”

Abinoam Jr.

On Thu, May 15, 2014 at 9:57 AM, Arup R.

On Thursday, May 15, 2014 01:42:28 PM you wrote:

=> {5=>“five”}

KeyError: key not found: 1
my_var = a[1] || “My Default Value”

Thanks Abinoam for your reply. I actually looking to create an Array,
whose
default elements will be different hash( means object_id should be
different
)
. So that on runtime I can access any array element, and add key to
the
hash, with a value.

Look the answer -

. The similar thing I am looking for an
array if can be implemented. If Array#default= exist, then all problem
can
be solved, but it exist for Hash, not Array.

why not use a hash then?

Hi Arup,

Repeating myself and agreeing with Craig, you’re in need of a Hash not
an Array.

But, the same “or” workaround from my previous email will fit.

my_var = a[ix] || Hash.new

Everytime a[ix] is nil it will return a fresh new Hash (with different
object id).

Hope it helps you.

Best regards,
Abinoam Jr.

On Thu, May 15, 2014 at 6:06 PM, Arup R.
[email protected] wrote:

Look the answer -
ruby - Best way to check for nil and update hash value - Stack Overflow
. The similar thing I am looking for an
array if can be implemented. If Array#default= exist, then all problem can
be solved, but it exist for Hash, not Array.

array = []

def array.get(i)
self[i] ||= {}
end

Alternatively

class <<array
alias _get []
def h=_get(i) or self[i] = {} end
end

irb(main):010:0> array[0][:foo]=:bar
=> :bar
irb(main):011:0> array[3][:bar]=:foo
=> :foo
irb(main):012:0> array
=> [{:foo=>:bar}, nil, nil, {:bar=>:foo}]

Cheers

robert

even shorter, without class and alias

def array.
super or self[i] = {}
end

On Tue, May 20, 2014 at 6:56 AM, Robert K.
[email protected] wrote:

class <<array
alias _get []
def h=_get(i) or self[i] = {} end
end

This is exquisite. :slight_smile:

tamouse m. wrote in post #1146722:

On Tue, May 20, 2014 at 6:56 AM, Robert K.
[email protected] wrote:

class <<array
alias _get []
def h=_get(i) or self[i] = {} end
end

This is exquisite. :slight_smile:
Not quite :wink: It would return wrong results if value at passed index is
false' or nil’.

David U. wrote in post #1146835:

Did you missed the fetch method ?
Class: Array (Ruby 2.1.2)

Thanks for the reminder!

You can pass the default value as a second argument.
In your case:

array = Array.new(4) { {} }
array.fetch(11, {}) # returns {}

If you insist on use of [] method you can simply modify class or
singleton like

class Array
def
fetch(ix, {})
end
end

This has two disadvantages:

  • You always create a new Hash even if the element exists.
  • The empty Hash is not inserted into the Array which is what the OP
    wants.

Better use the block form with an assignment:

irb(main):018:0> a.fetch(1){|i| a[i]={}}[:foo]=:bar
=> :bar
irb(main):019:0> a
=> [nil, {:foo=>:bar}]
irb(main):020:0> a[1]
=> {:foo=>:bar}
irb(main):021:0> a.fetch(1){|i| a[i]={}}[:bar]=:foo
=> :foo
irb(main):022:0> a
=> [nil, {:foo=>:bar, :bar=>:foo}]

David U. wrote in post #1146837:

tamouse m. wrote in post #1146722:

On Tue, May 20, 2014 at 6:56 AM, Robert K.
[email protected] wrote:

class <<array
alias _get []
def h=_get(i) or self[i] = {} end
end

This is exquisite. :slight_smile:
Not quite :wink: It would return wrong results if value at passed index is
false' or nil’.

This objection is negligible in this case as only Hashes are inserted.
At least this is what I assume is the OP’s goal.

But in fact the solution using fetch is more exquisite (especially since
variable “h” is superfluous).

Did you missed the fetch method ?

You can pass the default value as a second argument.
In your case:

array = Array.new(4) { {} }
array.fetch(11, {}) # returns {}

If you insist on use of [] method you can simply modify class or
singleton like

class Array
def
fetch(ix, {})
end
end

Robert K. wrote in post #1146925:

David U. wrote in post #1146835:

class Array
def
fetch(ix, {})
end
end

This has two disadvantages:

  • You always create a new Hash even if the element exists.
  • The empty Hash is not inserted into the Array which is what the OP
    wants.

There’s one more: you change Array#[] which is a really bad idea. If at
all do

array = []

def array.
fetch(ie) { self[ie] = {} }
end

or create a subclass of Array.

Robert K. wrote in post #1146927:

There’s one more: you change Array#[] which is a really bad idea. If at
all do

array = []

def array.
fetch(ie) { self[ie] = {} }
end

or create a subclass of Array.

Yep, monkey-patching standard classes is not nice. I mentioned it if OP
insists on use Array#[] or set it on a singleton. Or I’d add on a
subclass as you recommend.

Robert K. wrote in post #1146925:

This has two disadvantages:

  • You always create a new Hash even if the element exists.

Definitely, however that was just a simplified example. Block form is a
bit less readable.

  • The empty Hash is not inserted into the Array which is what the OP
    wants.

Where do you see in the OP a requirement for storing an empty Hash into
an array ?

irb a.fetch(1){|i| a[i]={}}[:foo]=:bar
=> :bar
Your example doesn’t work as requested as it populates nil values for
all missing indexes. Taking the original example:

irb> array = Array.new(4) { {} }
=> [{}, {}, {}, {}]
irb> array.fetch(11) {|i| array[i]={}}
=> {}
irb> array
=> [{}, {}, {}, {}, nil, nil, nil, nil, nil, nil, nil, {}]
irb> array.fetch(7) {|i| array[i]={}}
=> nil

As you can see autovivified `nil’ is returned instead of an expected {},
ie. an empty hash.

David U. wrote in post #1146932:

Robert K. wrote in post #1146925:

This has two disadvantages:

  • You always create a new Hash even if the element exists.

Definitely, however that was just a simplified example. Block form is a
bit less readable.

You need it to avoid unnecessary object creation. Whether the worry is
worthwhile is probably debatable. I generally try to avoid unnecessary
work.

  • The empty Hash is not inserted into the Array which is what the OP
    wants.

Where do you see in the OP a requirement for storing an empty Hash into
an array ?

He wrote in post #1146155:

But I am looking for a way to get an empty hash ( for
first time ), for any index access from array ?

Later he wrote in post #1146161:

I was actually looking for Hash#default=
(Class: Hash (Ruby 2.1.1)…). Why
Array class don’t have any such method, like Hash class has ?
Hash#default= is very useful although.

So he wanted the same behavior as can be achieved with
Hash#default_proc, i.e. on first access insert a new Hash that is then
returned ob subsequent accesses.

irb a.fetch(1){|i| a[i]={}}[:foo]=:bar
=> :bar
Your example doesn’t work as requested as it populates nil values for
all missing indexes.

Well, you suggested Array#fetch. :slight_smile: That makes it disadvantage #4 of
the approach. :slight_smile:

As you can see autovivified `nil’ is returned instead of an expected {},
ie. an empty hash.

Right. Good catch.

My advice for OP:

If you have sparse indexes i.e. large gaps between entries I’d just go
with Hash and use Hash#default_proc according to the usual pattern:

data = Hash.new {|h,k| h[k] = {}}

If you use all (or most of) the indexes you have to cook this yourself,
e.g.

data = []

class <<data
alias _get []

def
_get(i) or self[i] = {}
end
end

And in the intended usage scenario “false” and “nil” are no issues.

Btw. strictly speaking there is no reliable way to determine from an
Array whether an index has been set (as opposed to what Hash provides -
you can distinguish the case where a key was explicitly set to nil or
just the default value was returned). So there is no other way as to
resort to the current field value.

The only alternative for reliably detecting key absence is using the
index or Array#fetch. But then you must ensure that for every access
past the last index all new indexes are filled immediately which seems a
bit wasteful to me:

data = []

class <<data
def
fetch(i) do
for j in self.size…i
self[j] = l = {}
end
l
end
end
end

Kind regards

robert

Robert K. wrote in post #1147030:

My advice for OP:

If you have sparse indexes i.e. large gaps between entries I’d just go
with Hash and use Hash#default_proc according to the usual pattern:

data = Hash.new {|h,k| h[k] = {}}

If you use all (or most of) the indexes you have to cook this yourself,
e.g.

data = []

class <<data
alias _get []

def
_get(i) or self[i] = {}
end
end

And in the intended usage scenario “false” and “nil” are no issues.

This will work for me. Thanks for your great explanation too. But I am
upset to see, these long discussion went on, which I was not aware of.
None
of these came to my Inbox.

@Robert did you get these updates from mailing list or Rubyforum ?

Arup R. wrote in post #1147038:

This will work for me. Thanks for your great explanation too.

You’re welcome!

But I am
upset to see, these long discussion went on, which I was not aware of.
None
of these came to my Inbox.

@Robert did you get these updates from mailing list or Rubyforum ?

I think the bridge is one way currently (ML → Forum it seems).

see Bridge to ruby-forum is back? - Ruby - Ruby-Forum