4
a = [ 'a' ]
b = [ 'b' ]

def c

    return [ 'c' ], [ 'd' ]

end

a, b += c # -> would be awesome, but gives syntax error

a, b = a + c.first, b + c.last # clunky and will call method twice...

# desired result
#
a == [ 'a', 'c' ]
b == [ 'b', 'd' ]

Right now I often find myself writing:

t, tt = c
a += t
b += tt

but that's kind of ugly if you ask me.

Edit: Single element arrays seemed to have confused some people since several answers below just don't answer the question. I have made it more clear by letting each array have at least 2 elements.

Edit2: I filed a feature request with ruby core to implement compound assignments on destructured arrays.

  • 1
    Please never ever modify answers the way you did it. Feel free to post your own answer in a case you think no one provided suits your need. – Aleksei Matiushkin Jul 18 '16 at 09:56
  • Ok, no worries, but it would actually answer the question with the edit... now it doesn't really answer this question.... –  Jul 18 '16 at 10:04
  • 1
    It is none of your concern, besides that you have re-stated a question after all presented answers were given. You have basically three options: accept an answer, up-/down-vote it and ignore it. You are not permitted to mess the answers up, even if you think that will improve them. – Aleksei Matiushkin Jul 18 '16 at 10:08
  • It's not "OK, no worries". @mudasobwa has explained why changing your question is a no-no, though I don't understand why you need be told, considering that those changes have rendered answers non-sensical. Letting your edit stand earns a downvote from me. – Cary Swoveland Jul 18 '16 at 14:23
  • Ok, I'll change it back. The answers where nonsensical already. The change I make was to make the structure stand out better, not to change it. –  Jul 18 '16 at 15:18
  • So, it's back. Your answer does not answer the question... how hard can it be –  Jul 18 '16 at 15:20
  • @CarySwoveland https://ideone.com/knubmy -> original question, your answer. return FALSE –  Jul 18 '16 at 15:24
  • I presume that, by "desired output: `a == [ 'a', 'c' ]"`, you mean that the array `a` is to be changed from `[ 'a' ]` to `['a', 'b']`. Is that correct? That `['a'] == ['a', 'b'] #=> false` is pretty obvious. I removed by downvote. – Cary Swoveland Jul 18 '16 at 17:02
  • The desired result is that after initializing `a` and `b` as shown in the question and defining `c` as shown above and doing a concise operation (eg. more concise than what is shown above in "Right now I often find myself writing"), the statements `a == [ 'a', 'c' ]` and `b == [ 'b', 'd' ]` shall be true, understanding that what is essential is that c will return a number of arrays equal to the number of variables that need updating, and that the elements in the arrays returned from `c` shall be concatenated to the arrays `a` and `b`. `c` can return arrays of arbitrary length. –  Jul 18 '16 at 17:23

4 Answers4

5
a,b = [a+b,c].transpose
  #=> [["a", "c"], ["b", "d"]] 
a #=> ["a", "c"] 
b #=> ["b", "d"] 
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
5
a, b = (a+b).zip(c)
# a => ["a", "c"]
# b => ["b", "d"]

Hope it helps.

Harsh Trivedi
  • 1,594
  • 14
  • 27
  • 2
    `(a+b).zip(c)`, no need for flatten. – Mladen Jablanović Jul 18 '16 at 05:25
  • Ohh true .. that's even better. Would you add your answer too. Or should I update my answer? – Harsh Trivedi Jul 18 '16 at 05:28
  • 1
    Very nice, an improvement over what I suggested! – Cary Swoveland Jul 18 '16 at 07:04
  • 1
    I see I didn't need flatten. `zip` and `transpose` are interchangeable whenever the two arrays are the same size. – Cary Swoveland Jul 18 '16 at 07:11
  • This doesn't work: it gives `a = ["a", ["c"]]` meaning it appends instead of concatenates. –  Jul 18 '16 at 10:41
  • To make your answers cleaner and more to the point, if someone suggests an improvement that you agree with, edit your answer to remove your inferior initial answer and incorporate the improvement, *just as though you had written that in the first place*. You can thank the person who made the suggestion in your answer, but I prefer doing that in a comment, where I'd also state that I incorporated the suggestion in my answer. That gives credit, but make the answer more focused (sort of like writing a book). – Cary Swoveland Jul 22 '16 at 05:50
  • @MladenJablanović thanks for the suggestion again. I have kept your suggested (which I found better) answer only. – Harsh Trivedi Jul 22 '16 at 08:48
  • @CarySwoveland Thanks for the remark. I have updated the answer according to your suggestion, keeping only the superior option. :) – Harsh Trivedi Jul 22 '16 at 08:48
3

Since there was a requirement “without temporary copies,” I would post the solution of inplace modification of any amount of arrays

a1 = [ 'a1' ]
a2 = [ 'a2' ]
a3 = [ 'a3' ]
aa = [ a1, a2, a3 ]
cc = [ 'c1', 'c2', 'c3' ]

aa.each_with_object(cc.each) { |e, memo| e << memo.next }
#⇒ #<Enumerator: ["c1", "c2", "c3"]:each> # do not care, it’s memo

[a1, a2, a3]
#⇒ [ ["a1", "c1"], ["a2", "c2"], ["a3", "c3"] ]

Whether cc array is for some reason an array of arrays, as it specified in the question, it should flattened on some step, depending on how it is supposed to be added to a arrays.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • This doesn't work: it gives `a = ["a", ["c"]]` meaning it appends instead of concatenates. However this one I managed to adapt it so it actually works: `[a,b].each_with_object(c.each) { |e, memo| e.concat memo.next }`. It's still pretty clunky and cryptic, so I doubt I would use it much for a simple method return destructure, but it does scale, fair play. –  Jul 18 '16 at 10:46
  • 1
    This perfectly does work for anybody who is able to read till the end and flatten what enumerator returns: `aa.each_with_object(cc.each) { |e, memo| e.<<(*memo.next) }`. – Aleksei Matiushkin Jul 18 '16 at 11:04
0

None of the answers so far works, so for now I came up with nest_concat! (deep_concat seemed a misnomer, since we only want one deep):

class Array

    def nest_concat! other

        each_with_index { |arr, i| self[i].concat other[ i ] }

    end

    def nest_concat other

        d = map( &:dup )
        each_with_index { |arr, i| d[i].concat other[ i ] }

        d

    end

end

Allows you to write:

a = [ 'a', 'b' ]
b = [ 'c', 'd' ]

def c

    return [ 'A', 'B' ], [ 'C', 'D' ]

end

[ a, b ].nest_concat! c
p a
p b

Gives

["a", "b", "A", "B"]
["c", "d", "C", "D"]
  • What it gives contradicts to “desired result” section in the question as by 13:00 CET. – Aleksei Matiushkin Jul 18 '16 at 11:00
  • No it doesn't. [This is the original question](https://stackoverflow.com/revisions/38428151/1), which never changed, just updated to help people not to misinterprete the code. This is the original question with this solution: https://ideone.com/q8IAA2 –  Jul 18 '16 at 11:08
  • `return [ 'a' ], [ 'b' ]` in Ruby returns `[ [ 'a' ], [ 'b' ] ]`, not `[ 'a', 'b' ]`. You just misinterpreted it like all the others. That's why the title of the question is about concatenating nested arrays. –  Jul 18 '16 at 11:10
  • Please stop making assumptions on what I did misinterpret and re-read the desired output in the question. Hint: `"c"` is not the same as `"b"`. – Aleksei Matiushkin Jul 18 '16 at 11:18
  • For `def my_method; return [ 'c' ], [ 'd' ]; end`, executing that method returns `["c", "d"]`. Same thing, of course, if you give the method another name, such as `c`. If you think we are all wrong about that, try it yourself. Incidentally, the keyword `return` is not needed here. – Cary Swoveland Jul 18 '16 at 16:55
  • No, executing that method returns `[ ['c'], ['d'] ]`. And no, return is not redundant: http://codepad.org/72lPqGgI –  Jul 18 '16 at 17:35