Hợp nhất và xen kẽ hai mảng trong Ruby


106

Tôi có mã sau:

a = ["Cat", "Dog", "Mouse"]
s = ["and", "&"]

Tôi muốn hợp nhất mảng sthành mảng asẽ cung cấp cho tôi:

["Cat", "and", "Dog", "&", "Mouse"]

Xem qua các tài liệu Ruby Array và Enumerable, tôi không thấy phương pháp nào như vậy sẽ thực hiện được điều này.

Có cách nào tôi có thể làm điều này mà không cần lặp qua từng mảng không?


a sẽ luôn có 3 phần tử và s hai? một số ví dụ khác sẽ hữu ích.
tokland

Câu trả lời:


171

Bạn có thể làm điều đó với:

a.zip(s).flatten.compact

4
Điều gì xảy ra nếu acó nhiều hơn 3 phần tử?
Michael Kohl

116
[ "A", "b"] .concat ([ "c", "d"]) # => [ "a", "b", "c", "d"]
Leo Romanovsky

30
@Leo, @chuck: nếu bạn đọc ví dụ, bạn sẽ thấy rằng Chris muốn xen kẽ các phần tử, không nối chúng. Về cơ bản, anh ấy muốn [a, s].transposengoại trừ việc hai hàng không phù hợp, để lại #zipnhư một giải pháp rõ ràng. Và tôi không tin ý của anh ấy là anh ấy thực sự quan tâm đến việc liệu có abị đột biến hay không ... Tôi không nghĩ anh ấy đang bình luận về một giải pháp chức năng và đột biến nào cả, anh ấy chỉ đang cố gắng mô tả mô hình.
DigitalRoss

15
+1 vì là người duy nhất thực sự đọc câu hỏi của blummin! > _ <
Matt Fletcher

5
Quan trọng hơn, nếu hai mảng có độ dài không bằng nhau thì sao? Đặc biệt nếu s là cái dài hơn? Tôi nghĩ rằng tôi có thể giả định một cách an toàn ví dụ mà Chris đưa ra không phải là dữ liệu thực tế mà anh ấy đang làm việc. hãy xem xét: [] .zip [1, 2] => nil (sẽ gặp khó khăn khi gọi #flatten về điều đó) [3,4] .zip ([1, 3, 5, 7]) => [[3 , 1], [4, 3]] (oops, đoán chúng ta không quan tâm đến vài yếu tố cuối cùng trong mảng 2)
hoff2

32

Điều này sẽ không đưa ra một mảng kết quả theo thứ tự mà Chris yêu cầu, nhưng nếu thứ tự của mảng kết quả không quan trọng, bạn có thể sử dụng a |= b. Nếu bạn không muốn đột biến a, bạn có thể viếta | b và gán kết quả cho một biến.

Xem tài liệu liên hợp đặt cho lớp Mảng tại http://www.ruby-doc.org/core/classes/Array.html#M000275 .

Câu trả lời này giả định rằng bạn không muốn các phần tử mảng trùng lặp. Nếu bạn muốn cho phép các phần tử trùng lặp trong mảng cuối cùng của mình, a += bhãy thực hiện thủ thuật. Một lần nữa, nếu bạn không muốn thay đổi a, hãy sử dụng a + bvà gán kết quả cho một biến.

Theo một số ý kiến ​​trên trang này, hai giải pháp này sẽ hoạt động với các mảng có kích thước bất kỳ.


Điều này chắc chắn có vẻ là tốt nhất.
ardavis

11
Điều này mang lại ["Cat", "Dog", "Mouse", "and", "&"], đó không phải là những gì OP muốn.
Andrew Grimm

Cuộc gọi tuyệt vời, Andrew. Tôi sẽ cập nhật câu trả lời của mình để nói rằng tôi đã không trả lời câu hỏi của Chris.
Michael Stalker

29

Nếu bạn không muốn trùng lặp, tại sao không chỉ sử dụng toán tử union :

new_array = a | s

1
Trao +1 cho giải pháp được đánh giá thấp, đơn giản, thanh lịch.
Giacomo1968

Tất nhiên nó trả lời câu hỏi! Câu hỏi là: "Tôi muốn hợp nhất mảng s thành mảng a"
Douglas

Giải pháp tốt - nhưng điều này làm thay đổi thứ tự của kết quả. Kết quả từ ssẽ ở cuối mảng mới.
Hendrik

1
Tuy nhiên, thứ tự của các phần tử sẽ không như những gì OP muốn.
tokland

6
s.inject(a, :<<)

s   #=> ["and", "&"]
a   #=> ["Cat", "Dog", "Mouse", "and", "&"]

Nó không cung cấp cho bạn thứ tự bạn yêu cầu, nhưng đó là một cách hay để hợp nhất hai mảng bằng cách thêm vào một mảng.


Tôi thích nó, ngắn gọn và sạch sẽ. :)
Nafaa Boutefer

6

Đây là một giải pháp cho phép xen kẽ nhiều mảng có kích thước khác nhau (giải pháp chung):

arr = [["Cat", "Dog", "Mouse", "boo", "zoo"],
 ["and", "&"],
 ["hello", "there", "you"]]

first, *rest = *arr; first.zip(*rest).flatten.compact
=> ["Cat", "and", "hello", "Dog", "&", "there", "Mouse", "you", "boo", "zoo"]

2
Đẹp! Một hạn chế, mảng đầu tiên phải dài nhất.
Brian Low,

@BrianLow bắt tuyệt vời!
Abdo

5

Nó không chính xác thanh lịch, nhưng nó hoạt động với các mảng ở bất kỳ kích thước nào:

>> a.map.with_index { |x, i| [x, i == a.size - 2 ? s.last : s.first] }.flatten[0..-2] 
#=> ["Cat", "and", "Dog", "&", "Mouse"]

+1 để xử lý các trường hợp cạnh kỳ lạ, tôi nghĩ i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2]sẽ có giá trị như nhau.
mu quá ngắn.

Tôi không chắc liệu OP có muốn thay thế and&, vì vậy tôi đã đưa anh ta theo đúng nghĩa đen nhất có thể, trong khi cho phép abất kỳ độ dài nào.
Michael Kohl

3

Làm thế nào về một giải pháp tổng quát hơn hoạt động ngay cả khi mảng đầu tiên không phải là dài nhất và chấp nhận bất kỳ số lượng mảng nào?

a = [
    ["and", "&"],
    ["Cat", "Dog", "Mouse"]
]

b = a.max_by(&:length)
a -= [b]
b.zip(*a).flatten.compact

 => ["Cat", "and", "Dog", "&", "Mouse"]

2

Để xử lý tình huống cả hai a& skhông có cùng kích thước:

a.zip(s).flatten.compact | s
  • .compactsẽ xóa nilkhi alớn hơns
  • | ssẽ thêm các mục còn lại từ skhi nào anhỏ hơns

1

Một cách để thực hiện xen kẽ và cũng đảm bảo rằng cái nào là mảng lớn nhất cho phương thức zip, là lấp đầy một trong các mảng nilcho đến kích thước mảng khác. Bằng cách này, bạn cũng đảm bảo phần tử nào của mảng sẽ ở vị trí đầu tiên:

preferred_arr = ["Cat", "Dog", "Mouse"]
other_arr = ["and","&","are","great","friends"]

preferred_arr << nil while preferred_arr.length < other_arr.length
preferred_arr.zip(other_arr).flatten.compact
#=> ["Cat", "and", "Dog", "&", "Mouse", "are", "great", "friends"]

1
Tốt hơn một chút: preferred_arr.zip(other_arr).flatten | other_arr(không chèn lấp bằng con số không)
Adam Fendley

-2
arr = [0, 1]
arr + [2, 3, 4]

//outputs [0, 1, 2, 3, 4]

5
xin lỗi ... không nhận thấy thứ tự cụ thể mà bạn muốn đầu ra. Xin lỗi vì đã cố gắng trợ giúp, sẽ không xảy ra nữa.
David Morrow
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.