คุณจะเพิ่มอาร์เรย์ไปยังอาร์เรย์อื่นใน Ruby ได้อย่างไรและไม่ได้ผลลัพธ์ที่มีหลายมิติ


474
somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

ฉันคาดหวัง

["some","thing","another","thing"]

6
มันคุ้มค่าที่จะพูด (เพื่อไม่ให้คุณเสียใจ แต่เพราะมันจะกัดคุณซ้ำแล้วซ้ำอีก) ว่าความคาดหวังของคุณเป็นปัญหาที่นี่ Ruby arrays (ซึ่งแตกต่างจาก say arrays ใน Perl) จะไม่ทำให้แบนโดยอัตโนมัติในบริบทเช่นนี้ นี่ไม่ใช่ข้อผิดพลาด: มันเป็นคุณสมบัติ
Telemachus

3
ri Array@flatten!ทำไมคำถามนี้ได้รับคะแนนเสียงมากมาย? เอกสารชัดเจนArray#flatten! แผ่เอง
yeyo

7
คำถามจะได้รับการโหวตถ้าพวกเขามีประโยชน์กับผู้ใช้ คำถามที่ง่ายที่สุดได้รับ upvotes มากที่สุดเพราะเป็นประโยชน์กับคนส่วนใหญ่
Ziggy

@ yo yo คุณไม่เพียงแค่คิดว่าการดำเนินการแบนฟรี?
Konstantin

@Konstantin op ไม่ได้มองหาทางเลือกหรือพูดคุยเกี่ยวกับปัญหาด้านประสิทธิภาพ op คาดหวังว่าจะได้รับผลลัพธ์ที่เขาหรือเธอไม่ได้รับเพราะflatten!ใช้งานไม่ได้ ในที่สุดคำถามสะท้อนถึงปัญหาตรรกะแทนที่จะเป็นปัญหาการเพิ่มประสิทธิภาพ ดูคำตอบของ pilcrow ด้านล่างสำหรับข้อมูลเพิ่มเติม
yeyo

คำตอบ:


713

คุณมีความคิดที่ใช้การได้ แต่#flatten!อยู่ในสถานที่ที่ผิด - มันทำให้เครื่องรับแบนดังนั้นคุณสามารถใช้มันเพื่อเปลี่ยน[1, 2, ['foo', 'bar']]เป็น[1,2,'foo','bar']เข้าไป

ฉันลืมวิธีการบางอย่างได้อย่างไม่ต้องสงสัย แต่คุณสามารถต่อกันได้:

a1.concat a2
a1 + a2              # creates a new array, as does a1 += a2

หรือ / ต่อท้าย :

a1.push(*a2)         # note the asterisk
a2.unshift(*a1)      # note the asterisk, and that a2 is the receiver

หรือ ประกบกัน :

a1[a1.length, 0] = a2
a1[a1.length..0] = a2
a1.insert(a1.length, *a2)

หรือผนวกและเรียบ :

(a1 << a2).flatten!  # a call to #flatten instead would return a new array

17
ทำได้ดีมากสำหรับการเป็นเพียงคนเดียวเท่านั้น (จาก 5 คนที่ฉันเห็น) ซึ่งชี้ให้เห็นว่ามีอะไรผิดปกติกับรหัสที่นำเสนอ +1
Mike Woodhouse

53
การใช้การพุชแทนที่จะเป็น concat จะช่วยหลีกเลี่ยงการสร้างอาเรย์ที่สามดังนั้นจึงเหมาะสำหรับอาร์เรย์ขนาดใหญ่
phatmann

8
ฉันรักการกดปุ่มดอกจัน สง่างามมาก.
orourkedd

14
@phatmann Concatenation กับArray#concatไม่ได้จัดสรรอาร์เรย์ใหม่ Concatenation กับArray#+ไม่
cbliard

5
สิ่งเดียวที่คำตอบนี้หายไปคือการเปรียบเทียบมาตรฐานของแต่ละวิธี +1!
Terra Ashley

205

คุณสามารถใช้+โอเปอเรเตอร์ได้!

irb(main):001:0> a = [1,2]
=> [1, 2]
irb(main):002:0> b = [3,4]
=> [3, 4]
irb(main):003:0> a + b
=> [1, 2, 3, 4]

คุณสามารถอ่านทั้งหมดเกี่ยวกับคลาสอาเรย์ได้ที่นี่: http://ruby-doc.org/core/classes/Array.html


15
โปสเตอร์ต้องการทราบวิธีเชื่อมต่อกับอาร์เรย์ที่มีอยู่ไม่ใช่สร้างอาร์เรย์ใหม่ที่รวมกันของสองอาร์เรย์
phatmann

1
หมายเหตุ: a+= bสร้างอาร์เรย์ใหม่:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
kbrock

1
@kbrock ถูกต้อง หากต้องรับมือกับอาร์เรย์ขนาดใหญ่คุณจะต้องดูpushวิธีการตามที่อธิบายโดย @pilcrow
Joshua Pinter

2
จำไว้ว่า+=สร้างวัตถุใหม่ ในตัวอย่างเช่น[1, 2].each_with_object([]) { |number, object| object+=number }อาเรย์ที่ว่างเปล่า[]จะถูกส่งคืน
Filip Bartuzi

1
รายการที่เพิ่มจะต้องเป็นอาร์เรย์
RousseauAlexandre

66

วิธีที่สะอาดที่สุดคือการใช้ Array # concatวิธี; มันจะไม่สร้างอาร์เรย์ใหม่ (ต่างจาก Array # + ซึ่งจะทำสิ่งเดียวกัน แต่สร้างอาร์เรย์ใหม่)

ส่งตรงจากเอกสาร ( http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat ):

concat (other_ary)

ผนวกองค์ประกอบของ other_ary เข้ากับตนเอง

ดังนั้น

[1,2].concat([3,4])  #=> [1,2,3,4]  

Array # concatจะไม่แบนอาร์เรย์หลายมิติถ้ามันถูกส่งผ่านเป็นอาร์กิวเมนต์ คุณจะต้องจัดการแยกต่างหาก:

arr= [3,[4,5]]
arr= arr.flatten   #=> [3,4,5]
[1,2].concat(arr)  #=> [1,2,3,4,5]

สุดท้ายคุณสามารถใช้อัญมณี Corelib ของเรา ( https://github.com/corlewsolutions/corelib ) ซึ่งเพิ่มผู้ช่วยเหลือที่มีประโยชน์ให้กับคลาสทับทิมหลัก โดยเฉพาะอย่างยิ่งเรามีวิธีArray # add_allซึ่งจะแผ่อาร์เรย์หลายมิติโดยอัตโนมัติก่อนที่จะทำการเชื่อมต่อ


1
คุณมักต้องการความไม่เปลี่ยนแปลงดังนั้นการสร้างอาร์เรย์ใหม่จึงเป็นแนวคิดที่ดีกว่า
vasilakisfil

5
"คุณมักต้องการความไม่เปลี่ยนแปลง" ไม่ถูกต้อง ในการพัฒนาซอฟต์แวร์เต็มเวลามากกว่า 20 ปีฉันได้ทำงานกับอาร์เรย์และคอลเลกชันทุกชนิดเป็นประจำทุกวัน บางครั้งคุณปรับเปลี่ยนอาร์เรย์ที่มีอยู่ในสถานที่ บางครั้งคุณต้องทำงานกับอินสแตนซ์ใหม่
โซลูชั่น Corlew

35

วิธีง่าย ๆ ที่ทำงานกับ Ruby version> = 2.0 แต่ไม่ใช่กับรุ่นเก่ากว่า:

irb(main):001:0> a=[1,2]
=> [1, 2]
irb(main):003:0> b=[3,4]
=> [3, 4]
irb(main):002:0> c=[5,6]
=> [5, 6]
irb(main):004:0> [*a,*b,*c]
=> [1, 2, 3, 4, 5, 6]

2
@Ikuty นี่คือทางออกที่ดีที่สุดที่ฉันพบคุณช่วยอธิบายสิ่งที่เกิดขึ้นกับ*ที่นี่ได้ไหม?
Abhinay

@Abhinay ผู้ประกอบการพลัดระเบิดอาร์เรย์เป็นองค์ประกอบจึงสร้างอาร์เรย์มิติเดียวในบรรทัดสุดท้าย
Omar Ali

[*a, *b]ล้มเหลวสำหรับทับทิมรุ่นเก่าเช่น 1.8.7 และเท่าที่ทับทิมต้องการบอกให้คุณทราบถึงชีวิต RHEL6 ยังคงอยู่ทำให้ Ruby 1.8 เป็นรุ่นเป้าหมายที่สำคัญมาก
Otheus

1
ฉันไม่คิดว่าเหตุผลที่ -1 คำตอบนี้จะได้รับ ไม่มีรุ่นทับทิมที่กล่าวถึงโดย OP รุ่นทับทิมที่กล่าวถึงอย่างชัดเจนในคำตอบดังนั้น ... คุณต้องการที่จะย้อนกลับเข้ากันได้กับรุ่นก่อนอัลฟา 0.0.0.0.1? นี่เป็นหนึ่งในวิธีแก้ปัญหาที่ดีขึ้นอยู่กับรุ่นทับทิม
Ludovic Kuty

1
เพียงเพื่อจุดว่าคำตอบนี้เป็นอย่างมาก 'คล้าย' to JavaScript ES6 สำนวนมากในการที่คุณจะทำ[...array1, ...array2]เพียงแค่ความทรงจำว่าsplatผู้ประกอบการในทับทิมจะเป็นแทน* ...ทำให้ง่ายต่อการจดจำ
sandre89

34

ลองสิ่งนี้มันจะรวมอาร์เรย์ของคุณเพื่อลบรายการที่ซ้ำกัน

array1 = ["foo", "bar"]
array2 = ["foo1", "bar1"]

array3 = array1|array2

http://www.ruby-doc.org/core/classes/Array.html

เอกสารเพิ่มเติมดูที่ "Set Union"


นี่คือหรือจะส่งคืนอาร์เรย์ที่ไม่มีองค์ประกอบที่ซ้ำกันนี่คือตัวอย่างของวิธีที่มันอาจจะไม่ทำในสิ่งที่เขาถาม "baz" สองตัวในอาร์เรย์แรกจะกลายเป็นหนึ่งและ "บาร์" ในอาร์เรย์ที่สองจะไม่ได้รับการเพิ่ม array1 = ["foo", "bar", "baz", "baz"] array2 = ["foo1", "bar1", "bar"] array3 = array1 | array2 array3 # => ["foo", "bar "," baz "," foo1 "," bar1 "]
Joshua Cheek

หรือดียิ่งขึ้น:array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
Joshua Pinter

33

นี่คือสองวิธีสังเกตในกรณีนี้ว่าวิธีแรกกำหนดอาเรย์ใหม่ (แปลเป็น somearray = somearray + Somarray อีกครั้ง)

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray += anotherarray # => ["some", "thing", "another", "thing"]

somearray = ["some", "thing"]
somearray.concat anotherarray # => ["some", "thing", "another", "thing"]

24
a = ["some", "thing"]
b = ["another", "thing"]

ที่จะผนวกbการaและเก็บผลในa:

a.push(*b)

หรือ

a += b

ไม่ว่าในกรณีใดaจะกลายเป็น:

["some", "thing", "another", "thing"]

แต่ในกรณีที่อดีตองค์ประกอบของbถูกผนวกเข้ากับที่มีอยู่อาร์เรย์และในกรณีหลังทั้งสองอาร์เรย์จะตัดแบ่งกันและผลที่ได้จะถูกเก็บไว้ในaa


2
โปรดทราบว่าa.push(*b)ไม่เหมือนกันa += bทุกประการ อดีตเพิ่มองค์ประกอบใหม่ให้กับอาร์เรย์ที่มีอยู่ aหลังสร้างอาร์เรย์ใหม่ที่มีองค์ประกอบทั้งหมดและกำหนดมัน คุณสามารถเห็นความแตกต่างถ้าคุณทำสิ่งที่ต้องการaa = aบันทึกการอ้างอิงaก่อนที่จะผนวกวิธีการอย่างใดอย่างหนึ่งและจากนั้นตรวจสอบในaaภายหลัง ในกรณีก่อนหน้านี้จะเปลี่ยนด้วยค่าใหม่ของaและในกรณีหลังจะยังคงไม่เปลี่ยนแปลง
Dave Hartnoll

20

(array1 + array2).uniq

วิธีนี้คุณจะได้รับองค์ประกอบ array1 ก่อน คุณจะไม่ได้รับข้อมูลซ้ำซ้อน


9

อธิบายอย่างละเอียดในคำตอบของ @ Pilcrow คำตอบที่เหมาะสมสำหรับอาร์เรย์ขนาดใหญ่คือconcat(+ ) เนื่องจากรวดเร็วและไม่จัดสรรวัตถุใหม่เพื่อรวบรวมขยะเมื่อทำงานภายในลูป

นี่คือมาตรฐาน:

require 'benchmark'

huge_ary_1 = Array.new(1_000_000) { rand(5_000_000..30_000_00) }

huge_ary_2 = Array.new(1_000_000) { rand(35_000_000..55_000_00) }

Benchmark.bm do |bm|
  p '-------------------CONCAT ----------------'
  bm.report { huge_ary_1.concat(huge_ary_2) }

  p '------------------- PUSH ----------------'
  bm.report { huge_ary_1.push(*huge_ary_2)  }
end

ผล:

       user     system      total        real
"-------------------CONCAT ----------------"
  0.000000   0.000000   0.000000 (  0.009388)
"------------------- PUSH ----------------"
  example/array_concat_vs_push.rb:13:in `block (2 levels) in <main>': stack level too deep (SystemStackError)

ที่คุณสามารถดูโดยใช้pushพ่นข้อผิดพลาด : stack level too deep (SystemStackError)เมื่ออาร์เรย์ที่มีขนาดใหญ่พอ


8

คำถามหลักคือ "วิธีการเชื่อมต่ออาร์เรย์ใน Ruby" แน่นอนคำตอบคือการใช้concatหรือ+ตามที่กล่าวไว้ในเกือบทุกคำตอบ

ส่วนขยายตามธรรมชาติของคำถามจะเป็น "วิธีดำเนินการเรียงต่อแถวที่ชาญฉลาดของอาร์เรย์ 2 มิติใน Ruby" เมื่อฉัน googled "ruby concatenate matrices" คำถาม SO นี้เป็นผลลัพธ์สูงสุดดังนั้นฉันคิดว่าฉันจะทิ้งคำตอบของฉันไว้ที่คำถาม (ไม่ได้รับการถาม แต่เกี่ยวข้อง) ที่นี่เพื่อลูกหลาน


ในบางแอปพลิเคชันคุณอาจต้องการ "เชื่อมต่อกัน" สองมิติสองแถวเรียงกัน สิ่งที่ต้องการ,

[[a, b], | [[x],    [[a, b, x],
 [c, d]] |  [y]] =>  [c, d, y]]

นี่คือสิ่งที่ชอบ "การเพิ่ม" เมทริกซ์ ตัวอย่างเช่นฉันใช้เทคนิคนี้เพื่อสร้างเมทริกซ์ adjacency เดียวเพื่อแสดงกราฟจากเมทริกซ์ขนาดเล็ก หากไม่มีเทคนิคนี้ฉันจะต้องวนซ้ำส่วนประกอบต่างๆในลักษณะที่อาจทำให้เกิดข้อผิดพลาดได้ง่ายหรือหงุดหงิดที่จะคิด ฉันอาจจะต้องทำeach_with_indexเช่น แทนฉันรวมซิปและแผ่ดังต่อไปนี้

# given two multi-dimensional arrays that you want to concatenate row-wise
m1 = [[:a, :b], [:c, :d]]
m2 = [[:x], [:y]]

m1m2 = m1.zip(m2).map(&:flatten)
# => [[:a, :b, :x], [:c, :d, :y]]

8

อีกวิธีหนึ่งในการทำมัน

[somearray, anotherarray].flatten
=> ["some", "thing", "another", "thing"]

flattenแผ่ทุกอย่างเท่าที่จะทำได้ซ้ำไปซ้ำมา แม้กระทั่งอาร์เรย์ที่ซ้อนกัน ดังนั้นหากsomearrayหรือanotherarrayมีอาร์เรย์ที่ซ้อนกันพวกเขาก็จะแบนเหมือนกัน นี่คือผลข้างเคียงที่มักไม่ได้มีไว้
hagello

5

["some", "thing"] + ["another" + "thing"]


ฉันไม่รู้เกี่ยวกับประสิทธิภาพ แต่ใช้กับ Ruby 1.8 ได้ โดยทั่วไป[*a] + [*b]งาน
Otheus

ฉันไม่คิดว่า"another" + "thing"มันจะไปได้ตามที่คาดไว้
Alexis Wilke

5

หากข้อมูลใหม่อาจเป็นอาเรย์หรือสเกลาร์และคุณต้องการป้องกันไม่ให้ข้อมูลใหม่ซ้อนกันถ้าเป็นอาเรย์ตัวดำเนินการที่น่าประทับใจ! ส่งคืนสเกลาร์สำหรับสเกลาร์และรายการอาร์กิวเมนต์ที่ไม่ได้แพ็คสำหรับอาร์เรย์

1.9.3-p551 :020 > a = [1, 2]
 => [1, 2] 
1.9.3-p551 :021 > b = [3, 4]
 => [3, 4] 
1.9.3-p551 :022 > c = 5
 => 5 
1.9.3-p551 :023 > a.object_id
 => 6617020 
1.9.3-p551 :024 > a.push *b
 => [1, 2, 3, 4] 
1.9.3-p551 :025 > a.object_id
 => 6617020 
1.9.3-p551 :026 > a.push *c
 => [1, 2, 3, 4, 5] 
1.9.3-p551 :027 > a.object_id
 => 6617020 

4

ฉันประหลาดใจที่ไม่มีใครพูดถึงreduceซึ่งทำงานได้ดีเมื่อคุณมีอาร์เรย์:

lists = [["a", "b"], ["c", "d"]]
flatlist = lists.reduce(:+)  # ["a", "b", "c", "d"]

4
a = ['a', 'b']
b = ['c', 'd']
arr = [a, b].flatten

สิ่งนี้จะไม่ลบ dups ออก

a|b

ลบ dups


หมายเหตุ: นี่จะทำซ้ำอาร์เรย์ภายในทั้งหมดด้วยซ้ำ
Mirodinho

2

ฉันพบว่ามันง่ายกว่าที่จะผลักดันหรือผนวกอาร์เรย์แล้วเรียบให้เข้าที่เช่น:

somearray = ["some", "thing"]
anotherarray = ["another", "thing"]
somearray.push anotherarray # => ["some", "thing", ["another", "thing"]]
#or
somearray << anotherarray # => ["some", "thing", ["another", "thing"]]
somearray.flatten!  # => ["some", "thing", "another", "thing"]
somearray # => ["some", "thing", "another", "thing"]

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.