# How to dynamically create a NamedTuple for double splatting?

**URL:** <https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351>\
**Category:** Help & Support\
**Created:** [November 11, 2019, 4:45am UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351 "2019-11-11T04:45:50Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![girng\_github](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/girng_github/32/571_2.png) [@girng\_github](https://forum.crystal-lang.org/u/girng_github)\
**Post date:** [November 11, 2019, 4:45am UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351/1 "2019-11-11T04:45:50Z")

</div>

```auto
def generate_item_by_id(id, quantity = 1, cold_res = 0, fire_res = 0, lightning_res = 0)
  {id, quantity, cold_res, fire_res, lightning_res}
end

mods_to_roll = {"cold_res", "fire_res", "lightning_res"}
mods = Hash(String, Int32).new

3.times do
  mods[mods_to_roll[rand(mods_to_roll.size)]] = 20
end

pp mods

pp generate_item_by_id(5, **NamedTuple.from(mods))

```

[https://play.crystal-lang.org/#/r/7ztn](https://play.crystal-lang.org/#/r/7ztn)

```auto
In /usr/lib/crystal/named_tuple.cr:53:24

 53 | NamedTuple.new(**{{T}}).from(hash)
                         ^
Error: undefined constant T

```

I believe this doesn’t work because `NamedTuple` needs the keys and types. However, they are chosen at random.

I don’t know if I am going about this the right way, though. Any tips / advice is appreciated

---

<div class="post-metadata">

**Author:** ![watzon](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/watzon/32/481_2.png) [@watzon](https://forum.crystal-lang.org/u/watzon)\
**Post date:** [November 11, 2019, 6:52am UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351/2 "2019-11-11T06:52:54Z")

</div>

NamedTupeles can’t be created dynamically (outside of macro land) so you definitely need to rethink things. If this is a one off the solution is super easy. You know the number of properties you’re assigning, so just randomly generate that number of items and assign them manually.

[https://play.crystal-lang.org/#/r/7zuy](https://play.crystal-lang.org/#/r/7zuy)

If you need a more robust solution, macros are probably the answer.

---

<div class="post-metadata">

**Author:** ![girng\_github](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/girng_github/32/571_2.png) [@girng\_github](https://forum.crystal-lang.org/u/girng_github)\
**Post date:** [November 11, 2019, 7:47am UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351/3 "2019-11-11T07:47:44Z")

</div>

Thanks @watzon  
I might have to rethink this

I have an idea where I can pass in a Hash, iterate over it, then update the local variables to their respective value? So if `fire_res`'s isn’t in the Hash, its local variable (`fire_res`) gets set to its default value. If it is in the Hash, it’s set to the value it has in the Hash.

Seems hacky, but I think it might work?

---

<div class="post-metadata">

**Author:** ![watzon](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/watzon/32/481_2.png) [@watzon](https://forum.crystal-lang.org/u/watzon)\
**Post date:** [November 11, 2019, 7:50am UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351/4 "2019-11-11T07:50:37Z")

</div>

Won’t work without a macro. Technically you could probably use `method_missing` to do that, but it wouldn’t be recommended. If each item is an individual class that inherits from a parent `Item` class it would be pretty easy to write a generator method in the base class that iterates over class variables.

---

<div class="post-metadata">

**Author:** ![girng\_github](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/girng_github/32/571_2.png) [@girng\_github](https://forum.crystal-lang.org/u/girng_github)\
**Post date:** [November 11, 2019, 8:01am UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351/5 "2019-11-11T08:01:36Z")

</div>

@watzon Check this out!! I got something working!!

```auto
def generate_item_by_id(id, mods)
  quantity = 1
  cold_res = 0
  fire_res = 0
  lightning_res = 0
  
  cold_res = mods["cold_res"] if mods["cold_res"]?
  fire_res = mods["fire_res"] if mods["fire_res"]?
  lightning_res = mods["lightning_res"] if mods["lightning_res"]?
        
  {id, quantity, cold_res, fire_res, lightning_res}
end
      
mods_to_roll = {"cold_res", "fire_res", "lightning_res"}   
mods = Hash(String, Int32).new
      
3.times do
  mods[mods_to_roll[rand(mods_to_roll.size)]] = 20
end
      
pp generate_item_by_id(5, mods)

```

[https://play.crystal-lang.org/#/r/7zvh/edit](https://play.crystal-lang.org/#/r/7zvh/edit)

What do you think?

---

<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:** [November 11, 2019, 6:18pm UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351/6 "2019-11-11T18:18:09Z")

</div>

I think what you’re looking for in your most recent example is `Hash#fetch`:

```auto
def generate_item_by_id(id, mods)
  quantity = 1
  cold_res = mods.fetch "cold_res", default: 0
  fire_res = mods.fetch "fire_res", default: 0
  lightning_res = mods.fetch "lightning_res", default: 0
        
  {id, quantity, cold_res, fire_res, lightning_res}
end

mods_to_roll = {"cold_res", "fire_res", "lightning_res"}   
mods = Hash(String, Int32).new

3.times do
  mods[mods_to_roll[rand(mods_to_roll.size)]] = 20
end

pp generate_item_by_id(5, mods)

```

[https://play.crystal-lang.org/#/r/7zxx](https://play.crystal-lang.org/#/r/7zxx)

I suspect that this could be more easily solved with a struct, though. How do you get your hash in the first place?

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [November 11, 2019, 6:38pm UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351/7 "2019-11-11T18:38:48Z")

</div>

I think you shouldn’t be using `Hash` nor `NamedTuple`, you want a type to represent an item or the item description.

---

<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:** [November 11, 2019, 8:09pm UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351/8 "2019-11-11T20:09:57Z")

</div>

Following up on what @asterite said, here are a couple examples.

> **Minor Modifications**
>
> ```auto
> struct Item
> getter id : Int32
> property quantity = 1
> getter cold_res : Int32
> getter fire_res : Int32
> getter lightning_res : Int32
>   
> def initialize(@id, @cold_res = 0, @fire_res = 0, @lightning_res = 0)
> end
>   
> def initialize(@id, mods)
> @cold_res = mods.fetch "cold_res", default: 0
> @fire_res = mods.fetch "fire_res", default: 0
> @lightning_res = mods.fetch "lightning_res", default: 0
> end
> end
> 
> mods_to_roll = {"cold_res", "fire_res", "lightning_res"}   
> mods = Hash(String, Int32).new
>       
> 3.times do
> mods[mods_to_roll[rand(mods_to_roll.size)]] = 20
> end
> 
> pp Item.new(5, mods)
> 
> ```

[Play Link](https://play.crystal-lang.org/#/r/7zyi)

> **Significant Modifications**
>
> ```auto
> enum DamageType
> Cold
> Fire
> Lightning
> # Try uncommenting the next line to see how you can easily add a new damage type
> # Acid
> end
> 
> class Item
> getter id : Int32
> property quantity = 1
> private getter resistances = Hash(DamageType, Int32).new
> 
> def initialize(@id, @resistances)
> end
> 
> def resistance(to damage_type : DamageType)
> resistances.fetch(damage_type, default: 0)
> end
> end
> 
> mods_to_add = DamageType.values # for illustration, we'll just do all damage types
> mods = Hash(DamageType, Int32).new
> 
> mods_to_add.each do |mod|
> mods[mod] = 20 if rand(100) > 50 # set resistance 50% of the time
> # Note: do *not* use the exact random code above. I'm certain there are better ways to do that.
> end
> 
> item = Item.new(5, mods)
> 
> pp item
> mods_to_add.each do |mod|
> puts "Resistance to #{mod}: #{item.resistance to: mod}"
> end
> 
> ```

[Play Link](https://play.crystal-lang.org/#/r/8004)

I expect you’re using Named Tuples because 1) you’re familiar with them, and 2) you expect them to be faster. A couple replies:

1. You may be familiar with them, but it will be much easier to re-familiarize yourself with your code later if you use types that represent your data with class, method, and variable names that describe what the named class, method, or variable is _for_. Note the unnecessary use of `to:` in the “Significant Modifications” code. It will take a moment longer to type, but it will be quicker to read later when you’re debugging why (for example) lightning damage resistance is far too prevalent, making a particular enemy much easier to deal with than it should be.
2. Named Tuples may indeed be faster in this case. However, they may also not be that much faster. You should worry about making your code well-structured, correct, and easy to read before worrying about how fast it is. It’s much easier to go back and speed up your code than to go back and fix the bug hiding in your tricky optimization.

Anyway, if you just want to use Hashes and Named Tuples, you obviously can. We’re just trying to help Present You write code that is easier to handle for Future You (or any other developers you may eventually decide to hire/collaborate with).

---

<div class="post-metadata">

**Author:** ![girng\_github](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/girng_github/32/571_2.png) [@girng\_github](https://forum.crystal-lang.org/u/girng_github)\
**Post date:** [November 12, 2019, 2:14am UTC](https://forum.crystal-lang.org/t/how-to-dynamically-create-a-namedtuple-for-double-splatting/1351/9 "2019-11-12T02:14:46Z")

</div>

Thanks for the info everyone. I like the `.fetch` and `default` way!! I think I’m going to use that for now

I am using a Tuple because it works well for bracket notation access, its `.to_json` converts it into a string delimited list, and intertwines beautifully with `db.query` methods. Examples:

```auto
ItemQuery = "itemid, rpg_items_id, slot_position, q, equipped, sockets, identified, i_atk_speed, i_phys, i_crit_chance, crit_multiplier, i_hp, i_ms, i_cast_speed, in_item, socket_data, i_defense, to_life, quality, fire_res, cold_res, lightning_res, rare_name"
alias ItemTuple = {Int64, Int32, String, Int16, Int8, String, Int8, Int16, Int16, Int16, Int16, Int16, Int16, Int16, String, String, Int16, Int16, Int8, Int8, Int8, Int8, String}

```

```auto
  def db_get_user_items(client, tabid = 0)
    db.query_all "select #{ItemQuery} from rpg_user_items where rpg_character_id = ? and user_id = ? and in_stash = #{tabid} and hardcore = ? and ladder = ?", client.selected_characterid, client.user_id, client.hardcore, client.ladder, as: ItemTuple.types
  end

```

I tried to use a class, but it became too convoluted. Had to maintain a DB.mapping, `property` macros, create custom overloads for bracket notation access (which I couldn’t figure out how to do), create a `to_json` overload to convert all ivars into a delimited string, etc. Just became too much. A `Tuple` seems to do more stuff out of the box for what I need, and seems more powerful.

I use classes a lot, I just don’t think using a class for this is a good idea with my current knowledge of Crystal.

> I suspect that this could be more easily solved with a struct, though. How do you get your hash in the first place?

Depends on the type of item. I have static Hashes loaded that represent the modifications to select from based on what item is being used. In the example below, it’s `ModGlobalWeapons` or `ModGlobalArmors`. The [data](https://i.gyazo.com/12406ecdb4a811077496ce634f759b9b.png) is [stored](https://i.gyazo.com/418fdd288463acca6634476f54047e6e.png) in MYSQL and exported as `csv`, then converted into Hashes in Crystal.

Example:

```auto
      when "transmute_orb"
        raise "Item must be normal rarity." if current_item_mods.size > 0
        mod_rolls = Array(String).new
        query_update = QueryUpdate.new

        if main_on_item["type"] == "Weapons"
          mod_rolls = ModGlobalWeapons.dup
          mod_rolls = mod_rolls.shuffle.first(2)
        else # assume boots/armors
          mod_rolls = ModGlobalArmors.dup
          if main_on_item["type"] == "Gloves"
            mod_rolls << "i_atk_speed"
          end
          mod_rolls = mod_rolls.shuffle.first(2)
        end
        mod_rolls.each do |mod_key|
          mod_struct_main = GlobalItemModsMapping[mod_key]
          mod_struct = mod_struct_main[mod_struct_main.keys.shuffle.first]
          final_roll_value = rand(mod_struct.min_value..mod_struct.max_value)
          query_update[mod_struct.mod_type] = final_roll_value
          tuple_index = GlobalItemTuple.index(mod_struct.mod_type)
          client.stash[tabid_on].items[on_itemid] = modify_item_tuple(client.stash[tabid_on].items[on_itemid], tuple_index, final_roll_value)
        end
        if query_update.size > 0
          db.exec "update rpg_user_items set #{hash_to_delimited_query_list(query_update)} where itemid = ? and user_id = ?", on_itemid, client.user_id
          client.send ({a: 4, new_data: query_update, q: new_item_quantity, on_itemid: on_itemid, itemid: incoming_itemid}), "ITEMUPDATE"
          # Remove 1 quantity from the orb..
          client.stash[tabid].items[incoming_itemid] = modify_item_tuple(item, 3, new_item_quantity)
        else
          raise "Item use failed.."
        end

```
