# How to append to an Array that is a default Value of a Hash?

**URL:** <https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144>\
**Category:** Help & Support\
**Created:** [May 21, 2020, 2:58pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144 "2020-05-21T14:58:38Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![ducktape](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/ducktape/32/846_2.png) [@ducktape](https://forum.crystal-lang.org/u/ducktape)\
**Post date:** [May 21, 2020, 2:58pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/1 "2020-05-21T14:58:38Z")

</div>

I’m new to Crystal and using v0.34.

I expected that if an array is my default, that I could append to it as in `test2`. However the array doesn’t ‘stick’? Could someone explain the correct way to do this / why I’m seeing the results I see?

```auto
test = Hash(String, Array(Int32)).new(default_value: [] of Int32)
test["cats"] = test["cats"] << 3
test["cats"] << 4
p test 

test2 = Hash(String, Array(Int32)).new(default_value: [] of Int32)
test2["dogs"] << 1
test2["dogs"] << 3
p test2

```

Output in crystal play:

```auto
{}Hash(String, Array(Int32))
[3]Array(Int32)
[3, 4]Array(Int32)
{"cats" => [3, 4]}Hash(String, Array(Int32))

{}Hash(String, Array(Int32))
[1]Array(Int32)
[1, 3]Array(Int32)
{}Hash(String, Array(Int32))

```

---

<div class="post-metadata">

**Author:** ![krthr](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/krthr/32/755_2.png) [@krthr](https://forum.crystal-lang.org/u/krthr)\
**Post date:** [May 21, 2020, 3:06pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/2 "2020-05-21T15:06:04Z")

</div>

I think it’s because you have not initialized “dogs” array.

```crystal
test2 = Hash(String, Array(Int32)).new(default_value: [] of Int32)
test2["dogs"] = [] of Int32 # initialized
test2["dogs"] << 1
test2["dogs"] << 3
p test2

# {"dogs" => [1, 3]}

```

[https://play.crystal-lang.org/#/r/948v](https://play.crystal-lang.org/#/r/948v)

---

<div class="post-metadata">

**Author:** ![wrq](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/wrq/32/761_2.png) [@wrq](https://forum.crystal-lang.org/u/wrq)\
**Post date:** [May 21, 2020, 3:11pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/3 "2020-05-21T15:11:00Z")

</div>

@krthr beat me to it, I was just working on the same thing.

[https://play.crystal-lang.org/#/r/948z](https://play.crystal-lang.org/#/r/948z)

Welcome to Crystal, @ducktape! Yeah, so the default\_value doesn’t automatically instantiate that array unless asked to, and because the Hash’s V is the same type, it’s not complaining about the \<\< method.

The api docs under `Hash(K, V).new` say:

> Creates a new empty `Hash` where the _default\_value_ is returned if a key is missing.

Which is to say, the result of `test2["dog"] << 4 ` is basically `[] << 4` instead of the intended `test2["dog"] << 4` with the initialized array.  
It is rather strange behavior though, and I’m not sure if that’s conveyed well in the docs.

---

<div class="post-metadata">

**Author:** ![krthr](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/krthr/32/755_2.png) [@krthr](https://forum.crystal-lang.org/u/krthr)\
**Post date:** [May 21, 2020, 3:12pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/4 "2020-05-21T15:12:49Z")

</div>

hahaha sorry. Nice explanation btw…

Welcome @ducktape! Here is the url of Hash’s API: [https://crystal-lang.org/api/0.34.0/Hash.html#new(default\_value:V,initial\_capacity=nil)-class-method](https://crystal-lang.org/api/0.34.0/Hash.html#new(default_value:V,initial_capacity=nil)-class-method)

---

<div class="post-metadata">

**Author:** ![bcardiff](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/bcardiff/32/3_2.png) [@bcardiff](https://forum.crystal-lang.org/u/bcardiff)\
**Post date:** [May 21, 2020, 3:22pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/5 "2020-05-21T15:22:07Z")

</div>

Beware of the defualt\_value semantics. You will end up with a shared array.

`Hash.new` can a block to lazy initialize entries. That might be more helpful.

Also, relevant old issue: [https://github.com/crystal-lang/crystal/issues/4376](https://github.com/crystal-lang/crystal/issues/4376)

---

<div class="post-metadata">

**Author:** ![RespiteSage](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/respitesage/32/793_2.png) [@RespiteSage](https://forum.crystal-lang.org/u/RespiteSage)\
**Post date:** [May 21, 2020, 3:23pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/6 "2020-05-21T15:23:01Z")

</div>

Here’s something fun (a demonstration of @bcardiff’s point above) :

```crystal
test2 = Hash(String, Array(Int32)).new(default_value: [] of Int32)
test2["dogs"] << 1
test2["dogs"] << 3
p test2["dogs"] # => [1, 3]
p test2["cats"] # => [1, 3]
p test2 # => {}

```

[playground link](https://play.crystal-lang.org/#/r/9493)

The thing is, you _are_ actually appending to an array that is kept. However, you’re appending to the “default value” array that is returned when the key is not in the hash, not to the array “at” a particular key. If you want a new array that isn’t appended to, you can use the block constructor:

```crystal
test2 = Hash(String, Array(Int32)).new() { [] of Int32 }
# in the next two lines, Hash#[] returns a new array, which is appended to and then never assigned to anything
test2["dogs"] << 1
test2["dogs"] << 3
p test2["dogs"] # => []
p test2["cats"] # => []
p test2 # => {}

```

[playground link](https://play.crystal-lang.org/#/r/9498)

However, that doesn’t really get you what you want. What you’re trying to do with `default_value` isn’t really what those constructors are meant for. It’s more like if you want to count the number of occurrences of a particular word in a manuscript; if you use `default_value: 0`, then you can just query the hash about any string at all, and it’ll tell you 0 occurrences, unless you’ve actually given it an entry.

---

<div class="post-metadata">

**Author:** ![ducktape](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/ducktape/32/846_2.png) [@ducktape](https://forum.crystal-lang.org/u/ducktape)\
**Post date:** [May 21, 2020, 3:26pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/7 "2020-05-21T15:26:26Z")

</div>

Thanks for the replies! I only saw the constructor docs at the top of the api page, I didn’t know there was more at the bottom!

I think I get it. I ended up just testing if a key was there and if not initializing an array for it, but I think that [https://crystal-lang.org/api/0.34.0/Hash.html#new(block:Hash(K,V),K-\>V?=nil,\*,initial\_capacity=nil](https://crystal-lang.org/api/0.34.0/Hash.html#new(block:Hash(K,V),K-%3EV?=nil,*,initial_capacity=nil))-class-method would actually get me what I originally wanted.

---

<div class="post-metadata">

**Author:** ![ducktape](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/ducktape/32/846_2.png) [@ducktape](https://forum.crystal-lang.org/u/ducktape)\
**Post date:** [May 21, 2020, 3:28pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/8 "2020-05-21T15:28:33Z")

</div>

Actually, as @RespiteSage points out, the block method also doesn’t do what I want in this case.

While I’m here, are there any more complete docs for the `entry` api / is it like the rust hashmap `entry` api? Because that could also do what I want.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [May 21, 2020, 3:28pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/9 "2020-05-21T15:28:52Z")

</div>

You can do something like:

```auto
test2 = Hash(String, Array(Int32)).new { |h, k| h[k] = [] of Int32 }

test2["dogs"] << 1
test2["dogs"] << 3
p test2["dogs"] # => [1, 3]
p test2["cats"] # => []
p test2 # => {"dogs" => [1, 3], "cats" => []}

```

[https://play.crystal-lang.org/#/r/949j](https://play.crystal-lang.org/#/r/949j)

On missing value, it would invoke the block which would set that key to a new empty array.

---

<div class="post-metadata">

**Author:** ![ducktape](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/ducktape/32/846_2.png) [@ducktape](https://forum.crystal-lang.org/u/ducktape)\
**Post date:** [May 21, 2020, 3:30pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/10 "2020-05-21T15:30:28Z")

</div>

@Blacksmoke16 that works! So to sum up, the block method does indeed work.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [May 21, 2020, 3:31pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/11 "2020-05-21T15:31:56Z")

</div>

Well to be clear, _this_ specific block version works because it provides a reference the the hash itself, and the missing key. So you’re updating a reference to the actual hash, not just returning some arbitrary value.

---

<div class="post-metadata">

**Author:** ![wrq](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/wrq/32/761_2.png) [@wrq](https://forum.crystal-lang.org/u/wrq)\
**Post date:** [May 21, 2020, 3:39pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/12 "2020-05-21T15:39:08Z")

</div>

small snippet, to break down these various approaches:

[https://play.crystal-lang.org/#/r/949t](https://play.crystal-lang.org/#/r/949t)

```auto
# this does NOT work because in that block, [] is just an array... somewhere
test0 = Hash(String, Array(Int32)).new() { [] of Int32 }
test0["cats"] << 55 # this becomes [] << 55
p test0
 
# proof:
 
aux = test0["cats"] << 55
p aux
 
# the block is passed h (in our case, Hash(String, Array(Int32))) and k, (String)
# this works because h is our hash, so its updating itself here, whereas before, it's
# just returning a value and leaving the hash unchanged
test3 = Hash(String, Array(Int32)).new() { |h, k| h[k] = [] of Int32 }
test3["dogs"] << 55
p test3

```

output:

```auto
{}
[55]
{"dogs" => [55]}

```

---

<div class="post-metadata">

**Author:** ![ducktape](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/ducktape/32/846_2.png) [@ducktape](https://forum.crystal-lang.org/u/ducktape)\
**Post date:** [May 21, 2020, 3:42pm UTC](https://forum.crystal-lang.org/t/how-to-append-to-an-array-that-is-a-default-value-of-a-hash/2144/13 "2020-05-21T15:42:32Z")

</div>

Good summary @wrq, thank you all for the speedy replies!
