Home > Back-end >  Convert array into nested hash for each element
Convert array into nested hash for each element

Time:05-04

I have an array of strings like this, each string is separated by comma. They can be in any order. I would like to build a nested hash out of it. If an item already exists, then subsequent elements should be nested. Example:

["a", "a,b", "d", "d,e", "d,e,f", "a,b,foobar"]

I want the output to be -

  {
    a => {
      b => foobar
    },
    d => {
      e => f
    }
  }

I was thinking to iterate over each element, then split it on , and add to a temporary Hash in some recursive way, but I can't figure out how to get them nested.

CodePudding user response:

First, a function to loop through the array and split those strings into an array which is much easier to work with.

def nest_array(array)
  return array.each_with_object({}) do |string, hash|
    values = string.split(/,/)

    do_nesting(values, hash)
  end
end

Then a recursive function to deal with each individual entry. ['a'] becomes { 'a' => nil }, ['a', 'b'] becomes { 'a' => 'b' } and ['a', 'b', 'c'] recurses to make a new hash from ['b', 'c'].

def do_nesting(values, hash)
  if values.size <= 2
    hash[values[0]] = values[1]
  else
    hash[values.shift] = do_nesting(values, {})
  end

  return hash
end

CodePudding user response:

This should get you started.

def hashify(arr)
  # Get the first character from everything and make the hash.
  prefixes = arr.group_by { |x| x[0] }

  prefixes.transform_values do |inner|
    # Exclude the value that's just "a", i.e. that just shows the key
    # and no values inside.
    values = inner.map { |x| x[2..] }.grep_v(nil)
    # Recursively hashify the subvalues.
    hashify values
  end

end

Given the input

["a", "a,b", "d", "d,e", "d,e,f"]

This will produce

{"a"=>{"b"=>{}}, "d"=>{"e"=>{"f"=>{}}}}

Note that this isn't exactly what you want. Namely, "b" has been replaced with {"b" => {}} and the same for "f". It's not yet clear how you want to do that transformation. For instance, if the input is

["a,b", "a,c"]

Then what should the key a map to in the resulting hash? In my function above, it'll map to a hash {"b" => {}, "c" => {}}. It's up to you if this makes sense, or if you want to take the "first" encountered value, or perhaps make an array of all of them. Since that's not specified in the question, I'll leave that as an exercise to the reader.

  •  Tags:  
  • ruby
  • Related