Project

deepmap

0.0
Low commit activity in last 3 years
A long-lived project that still receives updates
Map functions over nested hash/arrays objects (e.g., YAML, JSON).
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 3.8, >= 3.8.0
~> 1.2
~> 0.22.0
 Project Readme

deepmap

Gem Version MIT

setup

[sudo] gem install deepmap

about

Ruby gem that adds three methods to the Hash and Array classes. Overview:

  • #deep_map - apply block to each key and value in object
  • #key_map - apply block to each key in object
  • #val_map - apply block to each value in object

These may be useful when you want to apply a function to each value (or key or pair) of a complex nested object (such as the result of parsing a YAML or JSON file), which can have hashes or arrays as subfields. The function currently needs to be passed in as a block (see usage).

usage

irb(main):001:0> require 'deepmap'
=> true

irb(main):002:0> test = { 1 => 4, 2 => [5, 6], 3 => { 4 => [1, 2, { 5 => 10 }] } }
=> {1=>4, 2=>[5, 6], 3=>{4=>[1, 2, {5=>10}]}}

irb(main):003:0> test.deep_map {|i| i.to_i * 2 }
=> {2=>8, 4=>[10, 12], 6=>{8=>[2, 4, {10=>20}]}}

irb(main):004:0> test.key_map {|i| i.to_i * 2 }
=> {2=>4, 4=>[5, 6], 6=>{8=>[1, 2, {10=>10}]}}

irb(main):005:0> test.val_map {|i| i.to_i * 2 }
=> {1=>8, 2=>[10, 12], 3=>{4=>[2, 4, {5=>20}]}}

Once you require 'deepmap', you can call any of the three provided functions on any (existing or new!) hash or array, as demonstrated above. You can also use the (&:method) shortcut to call a method on each object concisely:

irb(main):002:0> test = { 'a' => 'b', 'c' => ['d', 'e'], 'f' => { 'g' => ['h', 'i', { 'j' => 'k' }] } }
=> {"a"=>"b", "c"=>["d", "e"], "f"=>{"g"=>["h", "i", {"j"=>"k"}]}}

irb(main):003:0> test.deep_map(&:upcase)
=> {"A"=>"B", "C"=>["D", "E"], "F"=>{"G"=>["H", "I", {"J"=>"K"}]}}

irb(main):004:0> test.key_map(&:upcase)
=> {"A"=>"b", "C"=>["d", "e"], "F"=>{"G"=>["h", "i", {"J"=>"k"}]}}

irb(main):005:0> test.val_map(&:upcase)
=> {"a"=>"B", "c"=>["D", "E"], "f"=>{"g"=>["H", "I", {"j"=>"K"}]}}

Blocks may also take a second argument to make decisions based on both the key and the value, even while nominally mapping over just one of them. For key_map, the first argument is the key and the second is the value; for val_map it's the other way around, so single-argument blocks (including the &:method shortcut) keep working exactly as before:

irb(main):002:0> test = { 1 => 'a', -2 => 'b', 3 => 'c' }
=> {1=>"a", -2=>"b", 3=>"c"}

irb(main):003:0> test.key_map {|k, v| k.positive? ? k * 10 : v }
=> {10=>"a", "b"=>"b", 30=>"c"}

irb(main):004:0> test.val_map {|v, k| k.positive? ? v.upcase : v }
=> {1=>"A", -2=>"b", 3=>"C"}

key collisions

Since key_map and deep_map can map two different keys to the same new key, it's possible for the resulting hash to silently drop an entry (the last pair wins). By default this is checked for and reported with a warning; pass on_collision: to key_map/deep_map to change that behavior:

irb(main):002:0> { 1 => 'a', 2 => 'b' }.key_map {|k| 0 }
deepmap: key collision(s) produced duplicate key(s) [0]; earlier entries will be overwritten
=> {0=>"b"}

irb(main):003:0> { 1 => 'a', 2 => 'b' }.key_map(on_collision: :raise) {|k| 0 }
DeepMap::KeyCollisionError: deepmap: key collision(s) produced duplicate key(s) [0]; earlier entries will be overwritten

irb(main):004:0> { 1 => 'a', 2 => 'b' }.key_map(on_collision: :ignore) {|k| 0 }
=> {0=>"b"}

val_map never transforms keys, so it has no on_collision option. Passing anything other than :warn, :raise, or :ignore raises an ArgumentError.

development / testing

First, clone this repo:

git clone https://github.com/jeremywrnr/deepmap.git

To build from source, first install the project dependencies. This project uses bundler, the standard ruby gem management system. If you don't have it, try running gem install bundler. Once that is done:

bundle install

Now, we should be able to build the gem locally. This will build the local deepmap gem and link it in your path, so you can playing around with deepmap.

just build

This uses rspec, rubocop, and simplecov to run and check a suite of unit tests. To run the full suite:

just ci