1

I'm trying to convert an array into a hash by using some matching. Before converting the array into a hash, I want to merge the values like this

 "Desc,X1XXSC,C,CCCC4524,xxxs,xswd"

and create a hash from it. The rule is that, first value of the array is the key in Hash, in array there are repeating keys, for those keys I need to merge values and place it under one key. "Desc:" are keys. My program looks like this.

 p 'test sample application'
 str = "Desc:X1:C:CCCC:Desc:XXSC:xxxs:xswd:C:4524"
 arr = Array.new
 arr = str.split(":")
 p arr
 test_hash = Hash[*arr]
 p test_hash

I could not find a way to figure it out. If any one can guide me, It will be thankful.

2
  • 1
    Describe the rules for merging the values Commented Jun 26, 2013 at 10:47
  • Sure hold on i will edit Commented Jun 26, 2013 at 10:48

4 Answers 4

3

Functional approach with Facets:

require 'facets'
str.split(":").each_slice(2).map_by { |k, v| [k, v] }.mash { |k, vs| [k, vs.join] }
#=> {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}

Not that you cannot do it without Facets, but it's longer because of some basic abstractions missing in the core:

Hash[str.split(":").each_slice(2).group_by(&:first).map { |k, gs| [k, gs.map(&:last).join] }]
#=> {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}
Sign up to request clarification or add additional context in comments.

2 Comments

And we did it again, my each_with_object vs. your functional approach :)
@Sergio: Yeah, I noticed ;-) Now we need another one with inject, another one with each and we have the usual suspects rounded up again :-) If we had mash in the core I think there would be no discussion, but Hash[...] is so ugly and anti-OOP I understand nobody wants to use it.
2

A small variation on @Sergio Tulentsev's solution:

str = "Desc:X1:C:CCCC:Desc:XXSC:xxxs:xswd:C:4524"
str.split(':').each_slice(2).each_with_object(Hash.new{""}){|(k,v),h| h[k] += v}
# => {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}
  • str.split(':') results in an array; there is no need for initializing with arr = Array.new

  • each_slice(2) feeds the elements of this array two by two to a block or to the method following it, like in this case.

  • each_with_object takes those two elements (as an array) and passes them on to a block, together with an object, specified by:

  • (Hash.new{""}) This object is an empty Hash with special behaviour: when a key is not found then it will respond with a value of "" (instead of the usual nil).

  • {|(k,v),h| h[k] += v} This is the block of code which does all the work. It takes the array with the two elements and deconstructs it into two strings, assigned to k and v; the special hash is assigned to h. h[k] asks the hash for the value of key "Desc". It responds with "", to which "X1" is added. This is repeated until all elements are processed.

2 Comments

Thank you very much . Seems like I have quite a lot to learn about ruby . Thank you :)
@Kalanamith Added step by step explanation.
2

I believe you're looking for each_slice and each_with_object here

str = "Desc:X1:C:CCCC:Desc:XXSC:xxxs:xswd:C:4524"
hash = str.split(':').each_slice(2).each_with_object({}) do |(key, value), memo|
  memo[key] ||= ''
  memo[key] += value
end

hash # => {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}

1 Comment

Thank you very much. I will look in to what each_slice and each_with_object do. I'm still learning core ruby
1

Enumerable#slice_before is a good way to go.

str = "Desc:X1:C:CCCC:Desc:XXSC:xxxs:xswd:C:4524"
a = ["Desc","C","xxxs"] # collect the keys in a separate collection.
str.split(":").slice_before(""){|i| a.include? i}
# => [["Desc", "X1"], ["C", "CCCC"], ["Desc", "XXSC"], ["xxxs", "xswd"], ["C", "4524"]]
hsh = str.split(":").slice_before(""){|i| a.include? i}.each_with_object(Hash.new("")) do |i,h|
  h[i[0]] += i[1]
end
hsh
# => {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.