Làm thế nào để bạn thêm một mảng vào một mảng khác trong Ruby và không kết thúc với kết quả đa chiều?


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

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

Tôi mong đợi

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

6
Thật đáng để nói (không phải để cho bạn đau buồn, nhưng bởi vì nó sẽ cắn bạn nhiều lần) rằng sự mong đợi của bạn là vấn đề ở đây. Các mảng Ruby (không giống như các mảng trong Perl) không tự động làm phẳng trong các bối cảnh như thế này. Đây không phải là một lỗi: đó là một tính năng.
Telemachus

3
ri Array@flatten!Tại sao câu hỏi này nhận được nhiều phiếu bầu? Các tài liệu là rõ ràng Array#flatten! Flattens tự tại chỗ. Trả về nil nếu không có sửa đổi nào được thực hiện (nghĩa là mảng không chứa các phần phụ.)
yeyo

7
Câu hỏi nhận được upvote nếu chúng hữu ích cho người dùng. Các câu hỏi đơn giản nhất nhận được nhiều upvote nhất vì chúng hữu ích cho hầu hết mọi người.
Ziggy

@yeyo, bạn không nghĩ rằng hoạt động làm phẳng là miễn phí?
Konstantin

@Konstantin op không tìm kiếm giải pháp thay thế hoặc nói về các vấn đề hiệu suất, op đã mong đợi một kết quả mà anh ấy hoặc cô ấy không nhận được vì flatten!không làm việc như vậy. Cuối cùng, câu hỏi phản ánh một vấn đề logic hơn là một vấn đề tối ưu hóa. Xem câu trả lời của pilcrow dưới đây để biết thêm.
yeyo

Câu trả lời:


713

Bạn đã có một ý tưởng khả thi, nhưng #flatten!nó ở sai vị trí - nó làm phẳng bộ thu của nó, vì vậy bạn có thể sử dụng nó để biến [1, 2, ['foo', 'bar']]thành [1,2,'foo','bar'].

Tôi chắc chắn đã quên một số cách tiếp cận, nhưng bạn có thể nối lại :

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

hoặc thêm / nối thêm :

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

hoặc mối nối :

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

hoặc chắp thêm và làm phẳng :

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

17
được thực hiện tốt vì là người duy nhất (trong số 5 tôi có thể thấy), người thực sự đã chỉ ra những gì sai với mã được trình bày. +1
Mike Woodhouse

53
Sử dụng đẩy thay vì concat sẽ tránh việc tạo ra một mảng thứ ba, vì vậy điều này được ưu tiên cho các mảng lớn.
phatmann

8
Tôi thích sự thúc đẩy với dấu hoa thị. Rất thanh lịch.
orourkedd

14
@phatmann Ghép với Array#concatkhông phân bổ một mảng mới, Array#+
Ghép

5
Điều duy nhất câu trả lời này còn thiếu là so sánh điểm chuẩn của từng phương pháp. +1!
Terra Ashley

206

Bạn chỉ có thể sử dụng các +nhà điều hành!

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]

Bạn có thể đọc tất cả về lớp mảng ở đây: http://ruby-doc.org/core/groupes/Array.html


15
Người đăng muốn biết làm thế nào để nối với một mảng hiện có, không tạo ra một mảng mới là sự kết hợp của hai mảng.
phatmann

1
Lưu ý: a+= btạo một mảng mới:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
kbrock

1
@kbrock Đúng. Nếu xử lý các mảng lớn , bạn sẽ muốn xem pushphương thức như được mô tả bởi @pilcrow.
Joshua Pinter

2
hãy nhớ rằng +=tạo ra đối tượng mới. trong ví dụ như vậy, [1, 2].each_with_object([]) { |number, object| object+=number }mảng trống []sẽ được trả về
Filip Bartuzi 6/11/2015

1
Mục được thêm vào phải là một mảng
RousseauAlexandre

66

Cách tiếp cận sạch nhất là sử dụng Array # concat phương thức ; nó sẽ không tạo ra một mảng mới (không giống như Array # + sẽ làm điều tương tự nhưng tạo ra một mảng mới).

Trực tiếp từ các tài liệu ( http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat ):

concat (other_ary)

Nối các phần tử của other_ary vào chính nó.

Vì thế

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

Mảng # concat sẽ không làm phẳng một mảng nhiều chiều nếu nó được truyền vào dưới dạng đối số. Bạn sẽ cần xử lý riêng điều đó:

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

Cuối cùng, bạn có thể sử dụng đá quý corelib của chúng tôi ( https://github.com/corlewsolutions/corelib ) để thêm các trợ giúp hữu ích cho các lớp lõi Ruby. Cụ thể, chúng ta có một phương thức Array # add_all sẽ tự động làm phẳng các mảng nhiều chiều trước khi thực hiện concat.


1
Bạn thường muốn bất biến, vì vậy tạo ra một mảng mới là một ý tưởng tốt hơn.
vasilakisfil

5
"Bạn thường muốn bất biến" là không chính xác. Trong hơn 20 năm phát triển phần mềm toàn thời gian, tôi đã làm việc với tất cả các loại mảng và bộ sưu tập hàng ngày. Đôi khi bạn sửa đổi một mảng hiện có tại chỗ. Đôi khi bạn cần phải làm việc với một ví dụ mới.
Giải pháp Corlew

35

Phương pháp dễ dàng hoạt động với phiên bản Ruby> = 2.0 nhưng không phải với phiên bản cũ hơn:

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 Đây là giải pháp tao nhã nhất mà tôi tìm thấy, bạn có thể giải thích những gì đang xảy ra *ở đây không?
Abhinay

@ Abhinay toán tử plat phát nổ mảng thành các phần tử, do đó tạo ra một mảng một chiều ở dòng cuối cùng.
Omar Ali

[*a, *b]thất bại đối với các phiên bản cũ hơn của ruby, tức là 1.8.7. Và nhiều như Ruby muốn nói với bạn về sự sống của nó, RHEL6 vẫn được duy trì, biến Ruby 1.8 trở thành một phiên bản mục tiêu quan trọng.
Otheus

1
Tôi không nghĩ rằng điều đó biện minh cho câu trả lời -1 này. Không có phiên bản ruby ​​nào được OP đề cập, phiên bản ruby ​​được đề cập rõ ràng trong câu trả lời, vì vậy ... bạn có muốn tương thích ngược với phiên bản pre alpha 0.0.0.0.1 không? Đây là một trong những giải pháp tốt, tùy thuộc vào phiên bản ruby
Ludovic Kuty

1
Chỉ cần nói rằng câu trả lời này rất 'tương tự' với JavaScript ES6 rất thành ngữ mà bạn có thể làm [...array1, ...array2], chỉ cần nhớ rằng splattoán tử trong ruby ​​sẽ *thay thế .... Nó làm cho nó dễ nhớ hơn
sandre89

34

Hãy thử điều này, nó sẽ kết hợp các mảng của bạn loại bỏ trùng lặp

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

array3 = array1|array2

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

Xem thêm tài liệu tại "Set Union"


Đây là một hoặc, nó trả về một mảng không có phần tử trùng lặp, đây là một ví dụ về cách nó có thể không làm những gì anh ta yêu cầu, hai "baz" trong mảng đầu tiên được chuyển thành một và "thanh" trong mảng thứ hai không được thêm vào. mảng1 = ["foo", "bar", "baz", "baz"] mảng2 = ["foo1", "bar1", "bar"] mảng3 = mảng1 | mảng2 mảng3 # => ["foo", "bar "," baz "," foo1 "," bar1 "]
Joshua Cheek

Hoặc thậm chí tốt hơn:array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
Joshua Pinter

33

Dưới đây là hai cách, lưu ý trong trường hợp này là cách đầu tiên gán một mảng mới (dịch thành somearray = somearray + Anotherarray)

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

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

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

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

Để thêm bvào avà lưu trữ kết quả trong a:

a.push(*b)

hoặc là

a += b

Trong cả hai trường hợp, atrở thành:

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

nhưng trong trường hợp trước, các phần tử của bđược gắn vào amảng hiện có và trong trường hợp sau, hai mảng được nối với nhau và kết quả được lưu trữ trong a.


2
Lưu ý rằng a.push(*b)không chính xác như a += b. Cái trước thêm các phần tử mới vào mảng hiện có; cái sau tạo ra một mảng mới với tất cả các phần tử và gán nó cho a. Bạn có thể thấy sự khác biệt nếu bạn làm một cái gì đó như aa = ađể lưu ref vào atrước khi thêm phương thức và sau đó kiểm tra aasau đó. Trong trường hợp trước, nó thay đổi với giá trị mới avà trong trường hợp sau, nó vẫn không thay đổi.
Dave Hartnoll

20

(array1 + array2).uniq

Bằng cách này bạn có được các phần tử mảng1 trước. Bạn sẽ không nhận được bản sao.


9

Xây dựng câu trả lời của @ Pilcrow, câu trả lời phù hợp duy nhất cho các mảng lớn là concat(+ ) vì nó nhanh và không phân bổ một đối tượng mới được thu gom rác khi hoạt động trong một vòng lặp.

Đây là điểm chuẩn:

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

Các kết quả:

       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)

Như bạn có thể thấy bằng cách sử dụng pushném L ERI : stack level too deep (SystemStackError)khi các mảng đủ lớn.


8

Câu hỏi, về cơ bản, là "làm thế nào để ghép các mảng trong Ruby". Đương nhiên câu trả lời là sử dụng concathoặc+ như đã đề cập trong gần như mọi câu trả lời.

Một phần mở rộng tự nhiên cho câu hỏi sẽ là "làm thế nào để thực hiện nối hàng khôn ngoan các mảng 2D trong Ruby". Khi tôi googled "ma trận kết hợp ruby", câu hỏi SO này là kết quả hàng đầu vì vậy tôi nghĩ rằng tôi sẽ để lại câu trả lời của mình cho câu hỏi đó (không được nêu nhưng có liên quan) ở đây cho hậu thế.


Trong một số ứng dụng, bạn có thể muốn "nối" hai mảng 2D theo hàng. Cái gì đó như,

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

Đây là một cái gì đó giống như "tăng cường" một ma trận. Ví dụ, tôi đã sử dụng kỹ thuật này để tạo một ma trận kề, để biểu diễn một đồ thị trong một loạt các ma trận nhỏ hơn. Nếu không có kỹ thuật này, tôi sẽ phải lặp đi lặp lại các thành phần theo cách có thể dễ bị lỗi hoặc bực bội khi nghĩ về. Tôi có thể đã phải làm một each_with_indexví dụ. Thay vào đó tôi kết hợp ziplàm phẳng như sau,

# 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

Chỉ là một cách khác để làm điều đó.

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

flattenlàm phẳng mọi thứ càng xa càng tốt, đệ quy. Ngay cả các mảng lồng nhau. Do đó, nếu somearrayhoặc anotherarraychứa các mảng lồng nhau, chúng cũng bị làm phẳng. Đây là một tác dụng phụ thường không được dự định.
hagello

5

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


Tôi không biết về hiệu quả, nhưng điều này hiệu quả với Ruby 1.8. Nói chung, [*a] + [*b]các tác phẩm
Otheus

Tôi không nghĩ rằng "another" + "thing"nó sẽ làm việc như mong đợi.
Alexis Wilke

5

Nếu dữ liệu mới có thể là một mảng hoặc vô hướng và bạn muốn ngăn dữ liệu mới được lồng nếu nó là một mảng, toán tử splat là tuyệt vời! Nó trả về một vô hướng cho một vô hướng và một danh sách các đối số được giải nén cho một mảng.

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

Tôi ngạc nhiên không ai đã đề cập reduce, hoạt động tốt khi bạn có một mảng các mảng:

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

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

Điều này sẽ không loại bỏ dups, nhưng

a|b

loại bỏ dups.


Lưu ý: Điều này đệ quy làm phẳng tất cả các mảng bên trong là tốt.
Mirodinho

2

Tôi thấy dễ dàng hơn để đẩy hoặc nối các mảng và sau đó làm phẳng chúng tại chỗ, như vậy:

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"]

2

somearray = ["một số", "điều"]

Anotherarray = ["khác", "điều"]

somearray + otherarray

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.