Sao chép các mục mảng vào một mảng khác


918

Tôi có một mảng JavaScript dataArraymà tôi muốn đẩy vào một mảng mới newArray. Ngoại trừ tôi không muốn newArray[0]trở thành dataArray. Tôi muốn đẩy tất cả các mục vào mảng mới:

var newArray = [];

newArray.pushValues(dataArray1);
newArray.pushValues(dataArray2);
// ...

hoặc thậm chí tốt hơn:

var newArray = new Array (
   dataArray1.values(),
   dataArray2.values(),
   // ... where values() (or something equivalent) would push the individual values into the array, rather than the array itself
);

Vì vậy, bây giờ mảng mới chứa tất cả các giá trị của các mảng dữ liệu riêng lẻ. Có một số tốc ký như pushValuescó sẵn để tôi không phải lặp lại từng cá nhân dataArray, thêm từng mục một?



Đây phải là câu trả lời davidwalsh.name/combining-js-arrays
starikovs

Điều này có trả lời câu hỏi của bạn không? Cách hợp nhất hai mảng trong JavaScript và các mục không trùng lặp
HackerMan

Câu trả lời:


1248

Sử dụng hàm concat , như vậy:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayA.concat(arrayB);

Giá trị của newArraysẽ là [1, 2, 3, 4]( arrayAarrayBkhông thay đổi; concattạo và trả về một mảng mới cho kết quả).



16
Tôi đồng ý rằng thực hiện biểu diễn là rất tốt đẹp. NHƯNG không concat chính xác cho mục đích đó để concat đến mảng? Vì vậy, nó phải là tiêu chuẩn . Hoặc có những điều tốt hơn để làm với concat? Và nó có thể bị chậm chỉ vì triển khai kém công cụ JS của trình duyệt hoặc bất cứ nơi nào bạn đang sử dụng nó? Nó có thể được sửa chữa một ngày. Tôi sẽ chọn khả năng duy trì mã trên tối ưu hóa tốc độ hacky. Hmm ....
Bitterblue

3
Ngoài ra tôi chỉ điểm chuẩn tình huống: concat vs. push.apply. Google Chrome: fast (concat = người chiến thắng) , Opera: fast (concat = người chiến thắng) , IE: chậm hơn (concat = người chiến thắng) , Firefox: chậm (push.apply = người chiến thắng, nhưng chậm hơn 10 lần so với concat của Chrome) ... nói về việc triển khai công cụ JS tồi .
Bitterblue

15
Làm thế nào là nối hai mảng là câu trả lời được chấp nhận cho cách đẩy cái này sang cái khác?! Đó là hai hoạt động khác nhau.
kaqqao

@kaqqao vì pushsẽ không làm phẳng một loạt các giá trị. concatđạt được những gì câu hỏi yêu cầu.
WiseGuyEh

644

Với điều kiện mảng của bạn không lớn (xem cảnh báo bên dưới), bạn có thể sử dụng push()phương thức của mảng mà bạn muốn nối các giá trị. push()có thể lấy nhiều tham số để bạn có thể sử dụng apply()phương thức của nó để truyền mảng các giá trị được đẩy dưới dạng danh sách các tham số hàm. Điều này có lợi thế hơn so với việc sử dụng concat()thêm các phần tử vào mảng thay vì tạo một mảng mới.

Tuy nhiên, dường như đối với các mảng lớn (có thứ tự từ 100.000 thành viên trở lên), thủ thuật này có thể thất bại . Đối với các mảng như vậy, sử dụng một vòng lặp là một cách tiếp cận tốt hơn. Xem https://stackoverflow.com/a/17368101/96100 để biết chi tiết.

var newArray = [];
newArray.push.apply(newArray, dataArray1);
newArray.push.apply(newArray, dataArray2);

Bạn có thể muốn khái quát hóa điều này thành một hàm:

function pushArray(arr, arr2) {
    arr.push.apply(arr, arr2);
}

... hoặc thêm nó vào Arraynguyên mẫu:

Array.prototype.pushArray = function(arr) {
    this.push.apply(this, arr);
};

var newArray = [];
newArray.pushArray(dataArray1);
newArray.pushArray(dataArray2);

... hoặc mô phỏng push()phương thức ban đầu bằng cách cho phép nhiều tham số sử dụng thực tế là concat(), như push(), cho phép nhiều tham số:

Array.prototype.pushArray = function() {
    this.push.apply(this, this.concat.apply([], arguments));
};

var newArray = [];
newArray.pushArray(dataArray1, dataArray2);

Đây là phiên bản dựa trên vòng lặp của ví dụ cuối cùng, phù hợp với các mảng lớn và tất cả các trình duyệt chính, bao gồm IE <= 8:

Array.prototype.pushArray = function() {
    var toPush = this.concat.apply([], arguments);
    for (var i = 0, len = toPush.length; i < len; ++i) {
        this.push(toPush[i]);
    }
};

9
lưu ý: newArray.push.apply(newArray, dataArray1);cung cấp giống nhưArray.prototype.push.applay(newArray,dataArra1);

Điều này có tương thích với các trình duyệt cũ hơn không?
Damien Ó Ceallaigh


1
@ DamienÓCeallaigh: Có. Tất cả đều tương thích với các trình duyệt quay trở lại IE 6 và thậm chí sớm hơn.
Tim Down

Đây là mẫu mực của thực hành lập trình kém. Array.prototype.concatkhông phải là xấu, thay vào đó nó chỉ đơn giản là bị hiểu lầm và đôi khi bị sử dụng sai. Trong những trường hợp này, nó chỉ bị hiểu lầm, nhưng không bị lạm dụng.
Jack Giffin

446

Tôi sẽ thêm một câu trả lời "bằng chứng trong tương lai"

Trong ECMAScript 6, bạn có thể sử dụng cú pháp lây lan :

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);

console.log(arr1)

Cú pháp lây lan chưa được bao gồm trong tất cả các trình duyệt chính. Đối với khả năng tương thích hiện nay, thấy điều này (cập nhật liên tục) bảng tính tương thích .

Tuy nhiên, bạn có thể sử dụng cú pháp lây lan với Babel.js .

biên tập:

Xem trả lời của Jack Giffin bên dưới để biết thêm nhận xét về hiệu suất. Có vẻ như concat vẫn tốt hơn và nhanh hơn toán tử trải.


7
Bạn cũng có thể sử dụng toán tử trải nếu bạn đang sử dụng TypeScript. Nếu bạn nhắm mục tiêu ES5, nó sẽ biên dịch thành newArray.apply(newArray, dataArray1).
AJ Richardson

5
Lưu ý: nếu bạn cần kết quả trong mảng thứ ba (do đó không phải sửa đổi mảng1, vì câu hỏi ban đầu dường như yêu cầu), bạn có thể thực hiện newArray = [... Array1, ... Array2]
mờ

Ồ tôi không biết điều đó. Cảm ơn. Tôi đã thêm một bình luận chỉnh sửa.
Karel Bílek

Tương tự như cách concat sẽ hoạt động sau đó, với phần thưởng là sự bền bỉ (làm thay đổi mảng ban đầu).
Cướp

@robertmylne Toán tử trải rộng rõ ràng không sửa đổi mảng ban đầu, thay vào đó tạo ra một bản sao mới của tất cả các nội dung mảng được mô tả.
Jack Giffin

145

Tìm thấy một cách thanh lịch từ MDN

var vegetables = ['parsnip', 'potato'];
var moreVegs = ['celery', 'beetroot'];

// Merge the second array into the first one
// Equivalent to vegetables.push('celery', 'beetroot');
Array.prototype.push.apply(vegetables, moreVegs);

console.log(vegetables); // ['parsnip', 'potato', 'celery', 'beetroot']

Hoặc bạn có thể sử dụng spread operatortính năng của ES6:

let fruits = [ 'apple', 'banana'];
const moreFruits = [ 'orange', 'plum' ];

fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]

1
Đây không phải là những gì câu hỏi đã được yêu cầu. Câu hỏi là yêu cầu một cách để tạo một mảng mới mỗi lần, không sửa đổi một mảng cũ.
Jack Giffin

13
var a=new Array('a','b','c');
var b=new Array('d','e','f');
var d=new Array('x','y','z');
var c=a.concat(b,d)

Điều đó có giải quyết được vấn đề của bạn không?


13

Sau đây có vẻ đơn giản nhất với tôi:

var newArray = dataArray1.slice();
newArray.push.apply(newArray, dataArray2);

Vì "đẩy" có số lượng đối số thay đổi, bạn có thể sử dụng applyphương thức củapush hàm để đẩy tất cả các phần tử của mảng khác. Nó xây dựng một lệnh gọi để sử dụng đối số đầu tiên của nó ("newArray" ở đây) là "this" và các phần tử của mảng như các đối số còn lại.

Câu slicelệnh đầu tiên nhận được một bản sao của mảng đầu tiên, vì vậy bạn không sửa đổi nó.

Cập nhật Nếu bạn đang sử dụng phiên bản javascript với lát có sẵn, bạn có thể đơn giản hóa pushbiểu thức thành:

newArray.push(...dataArray2)

1
Cũng được đề cập ở đây tại MDN như một ví dụ cho "Hợp nhất hai mảng"
Wilt

12

Hàm bên dưới không có vấn đề với độ dài của mảng và hoạt động tốt hơn tất cả các giải pháp được đề xuất:

function pushArray(list, other) {
    var len = other.length;
    var start = list.length;
    list.length = start + len;
    for (var i = 0; i < len; i++ , start++) {
        list[start] = other[i];
    }
}

thật không may, jspref từ chối chấp nhận bài nộp của tôi, vì vậy đây là kết quả sử dụng chuẩn.js

        Name            |   ops/sec   |  ± %  | runs sampled
for loop and push       |      177506 |  0.92 | 63
Push Apply              |      234280 |  0.77 | 66
spread operator         |      259725 |  0.40 | 67
set length and for loop |      284223 |  0.41 | 66

Ở đâu

cho vòng lặp và đẩy là:

    for (var i = 0, l = source.length; i < l; i++) {
        target.push(source[i]);
    }

Đẩy Áp dụng:

target.push.apply(target, source);

toán tử lây lan:

    target.push(...source);

và cuối cùng, 'thiết lập độ dài và cho vòng lặp' là chức năng trên


Câu hỏi này đang tìm cách tạo ra một mảng mới mỗi lần, không sửa đổi một mảng hiện có.
Jack Giffin

10

𝗔𝗻𝗱

Đối với thực tế, một bài kiểm tra hiệu suất tại jsperf và kiểm tra một số thứ trong bảng điều khiển được thực hiện. Đối với nghiên cứu, trang web irt.org được sử dụng. Dưới đây là một tập hợp tất cả các nguồn này được đặt cùng với một hàm ví dụ ở phía dưới.

╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
Phương thức ║Concat║slice & push.apply ║ push.apply x2 ║ ForLoop S lây lan
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / giây ║179 104 76 81 28
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
Mảng thưa thớt ║YES! ║Chỉ cắt lát ║ không Có thể 2   no ║
Giữ thưa thớt array (1st arg) ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
Hỗ trợ ║MSIE 4║MSIE 5.5 MSIE 5.5 MSIE 4 ║ Chỉnh sửa 12
║ ( nguồn ) ║NNav 4║NNav 4,06 ║ ║ NNav 4,06 NNav 3 ║ MSIE  NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
Hành động giống như phản ứng║không Chỉ bị đẩy ║ CÓ! CÓ! Nếu có ║
║like một mảng ║ ║array (2nd arg) ║ ║ ║iterator 1   ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Nếu đối tượng giống như mảng không có thuộc tính Symbol.iterator , thì hãy thử
  để lây lan nó sẽ ném một ngoại lệ.
2 Phụ thuộc vào mã. Mã ví dụ sau "CÓ" duy trì độ thưa thớt.
function mergeCopyTogether(inputOne, inputTwo){
   var oneLen = inputOne.length, twoLen = inputTwo.length;
   var newArr = [], newLen = newArr.length = oneLen + twoLen;
   for (var i=0, tmp=inputOne[0]; i !== oneLen; ++i) {
        tmp = inputOne[i];
        if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;
    }
    for (var two=0; i !== newLen; ++i, ++two) {
        tmp = inputTwo[two];
        if (tmp !== undefined || inputTwo.hasOwnProperty(two)) newArr[i] = tmp;
    }
    return newArr;
}

Như đã thấy ở trên, tôi sẽ lập luận rằng Concat hầu như luôn luôn là cách để đi cả về hiệu suất và khả năng giữ lại độ thưa của các mảng dự phòng. Sau đó, đối với các lượt thích mảng (như DOMNodeLists thích document.body.children), tôi khuyên bạn nên sử dụng vòng lặp for vì đây là phương thức hiệu quả nhất thứ 2 và là phương thức khác duy trì các mảng thưa thớt. Dưới đây, chúng tôi sẽ nhanh chóng đi qua những gì có nghĩa là các mảng thưa thớt và thích mảng để xóa sự nhầm lẫn.

𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲

Lúc đầu, một số người có thể nghĩ rằng đây là một con sán và các nhà cung cấp trình duyệt cuối cùng sẽ tìm cách tối ưu hóa Array.prototype.push để đủ nhanh để đánh bại Array.prototype.concat. SAI LẦM! Array.prototype.concat sẽ luôn nhanh hơn (về nguyên tắc ít nhất) bởi vì nó là một bản sao-n-dán đơn giản trên dữ liệu. Dưới đây là sơ đồ trực quan đơn giản hóa về cách triển khai mảng 32 bit có thể trông như thế nào (xin lưu ý rằng việc triển khai thực tế phức tạp hơn rất nhiều)

Byte Dữ liệu tại đây
═════╬═══════════
0x00 ║ int nonNumericProperIESLpm = 0x00000000
0x01 ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int chiều dài = 0x00000001
0x01 ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int value Index = 0x00000000
0x01 ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ uintptr_t valuePulum = 0x38d9eb60 (hoặc bất cứ nơi nào có trong bộ nhớ)
0x01 ibid
0x02 ║ ibid
0x03 ║ ibid

Như đã thấy ở trên, tất cả những gì bạn cần làm để sao chép một cái gì đó gần như đơn giản như sao chép byte cho byte. Với Array.prototype.push.apply, nó không chỉ là một bản sao-n-dán đơn giản trên dữ liệu. ".Pyly" phải kiểm tra từng chỉ mục trong mảng và chuyển đổi nó thành một tập hợp các đối số trước khi chuyển nó sang Array.prototype.push. Sau đó, Array.prototype.push phải phân bổ thêm bộ nhớ mỗi lần và (đối với một số triển khai trình duyệt) thậm chí có thể tính toán lại một số dữ liệu tra cứu vị trí cho độ thưa.

Một cách khác để nghĩ về nó là điều này. Mảng nguồn một là một chồng giấy lớn được ghim lại với nhau. Mảng nguồn hai cũng là một chồng giấy tờ lớn khác. Nó sẽ nhanh hơn cho bạn

  1. Đi đến cửa hàng, mua đủ giấy cần thiết cho một bản sao của từng mảng nguồn. Sau đó đặt từng chồng giấy nguồn thông qua một máy sao chép và ghim hai bản sao lại với nhau.
  2. Đi đến cửa hàng, mua đủ giấy cho một bản sao của mảng nguồn đầu tiên. Sau đó, sao chép mảng nguồn vào giấy mới bằng tay, đảm bảo điền vào bất kỳ chỗ trống nào. Sau đó, quay trở lại cửa hàng, mua đủ giấy cho mảng nguồn thứ hai. Sau đó, đi qua mảng nguồn thứ hai và sao chép nó trong khi đảm bảo không có khoảng trống nào trong bản sao. Sau đó, ghim tất cả các giấy tờ sao chép lại với nhau.

Trong sự tương tự ở trên, tùy chọn # 1 đại diện cho Array.prototype.concat trong khi # 2 đại diện cho Array.prototype.push.apply. Chúng ta hãy kiểm tra điều này với một JSperf tương tự chỉ khác ở chỗ cái này kiểm tra các phương thức trên các mảng thưa thớt, không phải là mảng rắn. Người ta có thể tìm thấy nó ngay tại đây .

Do đó, tôi cho rằng trường hợp tương lai của hiệu năng cho trường hợp sử dụng cụ thể này không nằm ở Array.prototype.push, mà là ở Array.prototype.concat.

𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀

𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀

Khi một số thành viên nhất định của mảng bị thiếu. Ví dụ:

// This is just as an example. In actual code, 
// do not mix different types like this.
var mySparseArray = [];
mySparseArray[0] = "foo";
mySparseArray[10] = undefined;
mySparseArray[11] = {};
mySparseArray[12] =  10;
mySparseArray[17] = "bar";
console.log("Length:   ", mySparseArray.length);
console.log("0 in it:  ", 0 in mySparseArray);
console.log("arr[0]:   ", mySparseArray[0]);
console.log("10 in it: ", 10 in mySparseArray);
console.log("arr[10]   ", mySparseArray[10]);
console.log("20 in it: ", 20 in mySparseArray);
console.log("arr[20]:  ", mySparseArray[20]);

Ngoài ra, javascript cho phép bạn khởi tạo các mảng dự phòng một cách dễ dàng.

var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];

𝗔𝗿𝗿𝗮𝘆-

Giống như mảng là một đối tượng có ít nhất một thuộc lengthtính, nhưng không được khởi tạo với new Arrayhoặc []; Ví dụ, các đối tượng dưới đây được phân loại là giống như mảng.

{0: "foo", 1: "thanh", chiều dài: 2}
tài liệu.body.children
Uint8Array mới (3)
  • Điều này giống như mảng bởi vì mặc dù đó là một mảng (n) (gõ), việc ép buộc nó vào một mảng sẽ thay đổi hàm tạo.
(hàm () {trả về đối số}) ()

Quan sát những gì xảy ra bằng cách sử dụng một phương thức ép các lượt thích mảng thành các mảng như lát.

  • LƯU Ý: Đó là thực tế xấu khi gọi lát cắt trên các đối số chức năng vì hiệu suất.

Quan sát những gì xảy ra bằng cách sử dụng một phương thức không ép các lượt thích mảng thành các mảng như concat.


9

Với JavaScript ES6, bạn có thể sử dụng toán tử ... làm toán tử trải rộng, về cơ bản sẽ chuyển mảng thành giá trị. Sau đó, bạn có thể làm một cái gì đó như thế này:

const myArray = [1,2,3,4,5];
const moreData = [6,7,8,9,10];

const newArray = [
  ...myArray,
  ...moreData,
];

Mặc dù cú pháp ngắn gọn, tôi không biết làm thế nào điều này hoạt động bên trong và ý nghĩa hiệu suất là gì trên các mảng lớn.


2
Nếu bạn xem cách babel chuyển đổi nó , bạn sẽ thấy rằng nó không nên chậm hơn so với sử dụng Array.push.applykỹ thuật.
emil.c

1
@JackGiffin Tôi chỉ đề cập đến những gì Ryan đề cập rằng anh ta không biết nó hoạt động như thế nào trong nội bộ và ý nghĩa hiệu suất là gì, tôi thực sự không đề xuất phương pháp này. Trong mọi trường hợp, bạn đã thực hiện rất tốt câu trả lời của mình, nghiên cứu hay, thật tốt khi biết những chi tiết như vậy.
emil.c

9

Đây là cách ES6

var newArray = [];
let dataArray1 = [1,2,3,4]
let dataArray2 = [5,6,7,8]
newArray = [...dataArray1, ...dataArray2]
console.log(newArray)

Phương pháp trên là tốt cho hầu hết các trường hợp và các trường hợp không được xem xét concat, giống như bạn có hàng trăm ngàn mục trong mảng.

    let dataArray1 = [1,2,3,4]
    let dataArray2 = [5,6,7,8]
    let newArray = dataArray1.concat(dataArray2);
    console.log(newArray)


8

Có một số câu trả lời nói về Array.prototype.push.apply . Đây là một ví dụ rõ ràng:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
Array.prototype.push.apply(newArray, dataArray1); // newArray = [1, 2]
Array.prototype.push.apply(newArray, dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]

Nếu bạn có cú pháp ES6:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
newArray.push(...dataArray1); // newArray = [1, 2]
newArray.push(...dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]


4

Nếu bạn muốn sửa đổi mảng ban đầu, bạn có thể trải rộng và đẩy:

var source = [1, 2, 3];
var range = [5, 6, 7];
var length = source.push(...range);

console.log(source); // [ 1, 2, 3, 5, 6, 7 ]
console.log(length); // 6

Nếu bạn muốn đảm bảo chỉ các mục cùng loại đi vào sourcemảng (không trộn các số và chuỗi chẳng hạn), thì hãy sử dụng TypeScript.

/**
 * Adds the items of the specified range array to the end of the source array.
 * Use this function to make sure only items of the same type go in the source array.
 */
function addRange<T>(source: T[], range: T[]) {
    source.push(...range);
}

2

Ta có hai mảng a và b. mã những gì đã làm ở đây là mảng một giá trị được đẩy vào mảng b.

let a = [2, 4, 6, 8, 9, 15]

function transform(a) {
    let b = ['4', '16', '64']
    a.forEach(function(e) {
        b.push(e.toString());
    });
    return b;
}

transform(a)

[ '4', '16', '64', '2', '4', '6', '8', '9', '15' ]

1
Xin đừng chỉ đăng mã như một câu trả lời. Giải thích những gì mã làm và cách giải quyết vấn đề.
Patrick Hund

0

Thử cái này:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayB.reduce((pre, cur) => [...pre, ...cur], arrayA);
console.log(newArray)


-3

Đây là một mã làm việc và nó hoạt động tốt:

var els = document.getElementsByTagName('input'), i;
var invnum = new Array();
var k = els.length;
for(i = 0; i < k; i++){invnum.push(new Array(els[i].id,els[i].value))}
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.