0

I have two arrays like this:

a = [{'one'=>1, 'two'=>2},{'uno'=>1, 'dos'=>2}]
b = ['english', 'spanish']

I need to add a key-value pair to each hash in a to get this:

a = [{'one'=>1, 'two'=>2, 'language'=>'english'},{'uno'=>1, 'dos'=>2, 'language'=>'spanish'}]

I attempted this:

(0..a.length).each {|c| a[c]['language']=b[c]}

and it does not work. With this:

a[1]['language']=b[1]
(0..a.length).each {|c| puts c}

an error is shown:

NoMethodError (undefined method '[]=' for nil:NilClass)

How can I fix this?

4 Answers 4

1
a.zip(b){|h, v| h["language"] = v}
a # => [
  #      {"one"=>1, "two"=>2, "language"=>"english"},
  #      {"uno"=>1, "dos"=>2, "language"=>"spanish"}
  #    ]
Sign up to request clarification or add additional context in comments.

Comments

0

When the each iterator over your Range reaches the last element (i.e. a.length), you will attempt to access a nonexisting element of a.

In your example, a.length is 2, so on the last iteration of your each, you will attempt to access a[2], which doesn't exist. (a only contains 2 elements wich indices 0 and 1.) a[2] evaluates to nil, so you will now attempt to call nil['language']=b[2], which is syntactic sugar for nil.[]=('language', b[2]), and since nil doesn't have a []= method, you get a NoMethodError.

The immediate fix is to not iterate off the end of a, by using an exclusive Range:

(0...a.length).each {|c| a[c]['language'] = b[c] }

By the way, the code you posted:

(0..a.length).each {|c| puts c }

should clearly have shown you that you iterate till 2 instead of 1.

That's only the immediate fix, however. The real fix is to simply never iterate over a datastructure manually. That's what iterators are for.

Something like this, where Ruby will keep track of the index for you:

a.each_with_index do |hsh, i| hsh['language'] = b[i] end

Or, without fiddling with indices at all:

a.zip(b.zip(['language'].cycle).map(&:reverse).map(&Array.method(:[])).map(&:to_h)).map {|x, y| x.merge!(y) }

[Note: this last one doesn't mutate the original Arrays and Hashes unlike the other ones.]

Comments

0

The problem you're having is that your (0..a.length) is inclusive. a.length = 2 so you want to modify it to be 0...a.length which is exclusive.

On a side note, you could use Array#each_with_index like this so you don't have to worry about the length and so on.

a.each_with_index do |hash, index|
  hash['language'] =  b[index]
end

Comments

0

Here is another method you could use

 b.each_with_index.with_object(a) do |(lang,i),obj|
   obj[i]["language"] = lang
   obj
 end
 #=>[
     {"one"=>1, "two"=>2, "language"=>"english"}, 
     {"uno"=>1, "dos"=>2, "language"=>"spanish"}
    ]

What this does is creates an Enumerator for b with [element,index] then it calls with_object using a as the object. It then iterates over the Enumerator passing in each language and its index along with the a object. It then uses the index from b to find the proper index in a and adds a language key to the hash that is equal to the language.

Please know this is a destructive method where the objects in a will mutate during the process. You could make it non destructive using with_object(a.map(&:dup)) this will dup the hashes in a and the originals will remain untouched.

All that being said I think YAML would be better suited for a task like this but I am not sure what your constraints are. As an example:

yml = <<YML
-
  one: 1
  two: 2
  language: "english"
-
  uno: 1
  dos: 2
  language: "spanish"
YML
require 'yaml'
YAML.load(yml)
#=>[
     {"one"=>1, "two"=>2, "language"=>"english"}, 
     {"uno"=>1, "dos"=>2, "language"=>"spanish"}
    ]

Although using YAML I would change the structure for numbers to be more like language => Array of numbers by index e.g. {"english" => ["zero","one","two"]}. That way you can can access them like ["english"][0] #=> "zero"

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.