Why Does Ruby Parallel Assignment with Array of Strings Returns String

why does ruby parallel assignment with array of strings returns string

a, b = ["ho", "hey"] 

a is assigned the first element of the array, which is the string "ho". Nothing weird.

a, b = ["blerg"], ["baz"]

a, b = [["blerg"], ["baz"]]

These two are the same, as you can see by their return values. So a is assigned the first element, which is an array with one element: ["blerg"].

Similarly,

c, d = "foo", "bar"

Is the same as

c, d = ["foo", "bar"]

Why does parallel assignment of a single empty array assign multiple nils?

I believe this example will help you understand the problem:

[1] pry(main)> a, b, c = [1,2]
=> [1, 2]
[2] pry(main)> a
=> 1
[3] pry(main)> b
=> 2
[4] pry(main)> c
=> nil

Now back to your problem, you are trying to assign the elements in an empty array to variables, as a result, the three variables all get nil value.

ruby split string into array then split array elements into words

"hello world".split.map { |word| word[-1] + word[1..-2] + word[0] }.join(' ')
=> "oellh dorlw"

Swapping array elements using parallel assignment

It is expected. It follows from how ruby evaluates expressions.

deck[deck.index("A")], deck[deck.index("B")] = deck[deck.index("B")], deck[deck.index("A")]

Implies

deck[deck.index("A")], deck[deck.index("B")] = 'B', 'A'

Note: strings 'A' and 'B' here are for illustration only. Ruby doesn't create new string objects here. Which essentially is:

deck[deck.index("A")] = 'B' -> deck[0] = 'B' (deck = ['B', 'B', 'C'])
deck[deck.index("B")] = 'A' -> deck[0] = 'A' (deck = ['A', 'B', 'C'])

Array#index returns when it finds the first match.

Now,

deck[deck.index("B")], deck[deck.index("A")] = deck[deck.index("A")], deck[deck.index("B")]
-> deck[deck.index("B")], deck[deck.index("A")] = 'A', 'B'
-> deck[deck.index("B")] = 'A' -> deck[1] = 'A' (deck = ['A', 'A', 'C'])
-> deck[deck.index("A")] = 'B' -> deck[0] = 'B' (deck = ['B', 'A', 'C'])

Difference between += for Integers/Strings and For Arrays?

You see, names (variable names, like a and b) don't hold any values themselves. They simply point to a value. When you make an assignment

a = 5

then a now points to value 5, regardless of what it pointed to previously. This is important.

a = 'abcd'
b = a

Here both a and b point to the same string. But, when you do this

a += 'e'

It's actually translated to

a = a + 'e'
# a = 'abcd' + 'e'

So, name a is now bound to a new value, while b keeps pointing to "abcd".

a = [1,2,3,4]
b = a
a << 5

There's no assignment here, method << modifies existing array without replacing it. Because there's no replacement, both a and b still point to the same array and one can see the changes made to another.

Ruby: Hash assignment with concurrent loops

I think you just need to add a little map to my answer to your other similar question:

hash = Hash[quadrant.zip(array.each_slice(2).map { |a| @board[a.first][a.last] })]

Given a board like this:

@board = [
['11', '12', ... '18'],
['21', '22', ... '28'],
...
['81', '82', ... '88']
]

the above construct gives me a hash like this:

{
"upper_left" => "77",
"upper_right" => "75",
"lower_left" => "57",
"lower_right" => "55"
}

and that seems to be what you're looking for.

Ruby block taking array or multiple parameters

Ruby's block mechanics have a quirk to them, that is if you're iterating over something that contains arrays you can expand them out into different variables:

[ %w[ a b ], %w[ c d ] ].each do |a, b|
puts 'a=%s b=%s' % [ a, b ]
end

This pattern is very useful when using Hash#each and you want to break out the key and value parts of the pair: each { |k,v| ... } is very common in Ruby code.

If your block takes more than one argument and the element being iterated is an array then it switches how the arguments are interpreted. You can always force-expand:

[ %w[ a b ], %w[ c d ] ].each do |(a, b)|
puts 'a=%s b=%s' % [ a, b ]
end

That's useful for cases where things are more complex:

[ %w[ a b ], %w[ c d ] ].each_with_index do |(a, b), i|
puts 'a=%s b=%s @ %d' % [ a, b, i ]
end

Since in this case it's iterating over an array and another element that's tacked on, so each item is actually a tuple of the form %w[ a b ], 0 internally, which will be converted to an array if your block only accepts one argument.

This is much the same principle you can use when defining variables:

a, b = %w[ a b ]
a
# => 'a'
b
# => 'b'

That actually assigns independent values to a and b. Contrast with:

a, b = [ %w[ a b ] ]
a
# => [ 'a', 'b' ]
b
# => nil

I have a issue.. Appending

... if each string contains 60 numbers I want it to append into the a string until there are no more numbers

As written, you are unconditionally appending to x,y,z even after they hit your limit.
You need to add a conditional around this code:

   x << i if count == 1
y << i if count == 2
z << i if count == 3

so that it stops appending once it hits your limit.

By the looks of the else block that does nothing, I think you were headed in that direction:

  if x.length == 60
a << i if count == 1
a << i if count == 2
a << i if count == 3
else
x << i if count == 1
y << i if count == 2
z << i if count == 3
end

Even that, though, won't do exactly what you want.
You'll want to check the string you are appending to to see if it has hit your limit yet.

I'd suggest refactoring to make it cleaner:

 users.each do |i|
target_string = case count
when 1 then x
when 2 then y
when 3 then z
end

target_string = a if target_string.length == 60

target_string << i

if count == 3
count = 1
else
count += 1
end
end


Related Topics



Leave a reply



Submit