繁体   English   中英

将数组项复制到另一个数组

[英]Copy array items into another array

我有一个 JavaScript 数组dataArray ,我想将其推入一个新数组newArray 除了我不希望newArray[0]成为dataArray 我想将所有项目推入新数组:

var newArray = [];

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

甚至更好:

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
);

所以现在新数组包含单个数据 arrays 的所有值。是否有像pushValues这样的速记可用,这样我就不必遍历每个单独的dataArray ,一个一个地添加项目?

使用concat函数,如下所示:

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

newArray的值将为[1, 2, 3, 4]arrayAarrayB保持不变; concat为结果创建并返回一个新数组)。

如果您的数组不是很大(请参阅下面的警告),您可以使用要push()的数组的push()方法。 push()可以接受多个参数,因此您可以使用它的apply()方法将要推送的值数组作为函数参数列表传递。 与使用concat()将元素添加到数组而不是创建新数组相比,这具有优势。

但是,对于大型数组(100,000 名或更多成员的数量级),此技巧似乎会失败 对于此类数组,使用循环是更好的方法。 有关详细信息,请参阅https://stackoverflow.com/a/17368101/96100

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

您可能希望将其概括为一个函数:

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

...或将其添加到Array的原型中:

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

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

...或通过使用concat()push()一样允许多个参数来允许多个参数来模拟原始push()方法:

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

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

这是上一个示例的基于循环的版本,适用于大型数组和所有主流浏览器,包括 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]);
    }
};

在 ECMAScript 6 中,您可以使用Spread 语法

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

Spread 语法适用于所有主流浏览器(IE11 除外)。 有关当前的兼容性,请参阅此(不断更新的) 兼容性表

但是,有关性能的更多评论,请参阅下面的 Jack Giffin 回复。 似乎concat仍然比扩展运算符更好更快。

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

或者您可以使用 ES6 的spread operator功能:

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

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

以下对我来说似乎最简单:

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

由于“push”接受可变数量的参数,您可以使用push函数的apply方法来推送另一个数组的所有元素。 它使用第一个参数(此处为“newArray”)作为“this”,将数组元素作为剩余参数构造一个 push 调用。

第一个语句中的slice获取第一个数组的副本,因此您无需修改​​它。

更新如果您使用的是切片可用的 javascript 版本,您可以push表达式简化为:

newArray.push(...dataArray2)

𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵𝗔𝗻𝗱𝗕𝗲𝗻𝗰𝗵𝗺𝗮𝗿𝗸𝘀𝗣𝗼𝘄𝗲𝗿𝗳𝘂𝗹𝗹𝘆𝗦𝗵𝗼𝘄𝗧𝗵𝗮𝘁𝗖𝗼𝗻𝗰𝗮𝘁𝗜𝘀𝗧𝗵𝗲𝗕𝗲𝘀𝘁(对于原来的问题)

事实上, 在 jsperf 上进行性能测试并在控制台中检查了一些东西。 研究使用了网站 irt.org 下面是所有这些源的集合以及底部的示例函数。

╔═══════════════╦══════╦═════════════════╦═══════════════╦═════════╦══════════╗
║ Method        ║Concat║slice&push.apply ║ push.apply x2 ║ ForLoop ║Spread    ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║ mOps/Sec      ║179   ║104              ║ 76            ║ 81      ║28        ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║ Sparse arrays ║YES!  ║Only the sliced  ║ no            ║ Maybe2  ║no        ║
║ kept sparse   ║      ║array (1st arg)  ║               ║         ║          ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║ Support       ║MSIE 4║MSIE 5.5         ║ MSIE 5.5      ║ MSIE 4  ║Edge 12   ║
║ (source)      ║NNav 4║NNav 4.06        ║ NNav 4.06     ║ NNav 3  ║
 

 
 
  
  
 
  MSIE
 

 
  
 

 
 
  
  
 
  NNav
 

 
  ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║Array-like acts║no    ║Only the pushed  ║ YES!          ║ YES!    ║If have   ║
║like an array  ║      ║array (2nd arg)  ║               ║         ║iterator1 ║
╚═══════════════╩══════╩═════════════════╩═══════════════╩═════════╩══════════╝
1 If the array-like object does not have a  property, then trying
  to spread it will throw an exception.
2 Depends on the code. The following example code "YES" preserves sparseness.
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;
}

如上所示,我认为 Concat 几乎总是兼顾性能和保留备用阵列稀疏性的能力。 然后,对于阵列喜欢(例如像DOMNodeLists document.body.children ),我建议使用for循环,因为它既是第二最高效和保留稀疏数组唯一的其他方法。 下面,我们将快速了解稀疏数组和类数组的含义,以消除混淆。

𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲

起初,有些人可能认为这是侥幸,浏览器供应商最终会开始优化 Array.prototype.push 以使其足够快以击败 Array.prototype.concat。 错误的! Array.prototype.concat 总是更快(至少原则上是这样),因为它是对数据的简单复制粘贴。 下面是 32 位数组实现可能是什么样子的简化说服视觉图(请注意,实际实现要复杂得多)

Byte ║ Data here
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int length = 0x00000001
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueIndex = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (or whereever it is in memory)
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid

如上所示,复制类似内容所需要做的几乎就像逐字节复制一样简单。 使用 Array.prototype.push.apply,它不仅仅是对数据进行简单的复制粘贴。 “.apply”必须检查数组中的每个索引并将其转换为一组参数,然后再将其传递给 Array.prototype.push。 然后, Array.prototype.push 每次必须额外分配更多内存,并且(对于某些浏览器实现)甚至可能重新计算一些位置查找数据以进行稀疏化。

另一种思考方式是这样。 源阵列一是一大堆装订在一起的纸。 源阵二也是一大叠纸。 你会更快吗

  1. 去商店,购买足够的纸张来复制每个源阵列。 然后将每个源阵列的纸叠放在复印机中,并将生成的两份副本装订在一起。
  2. 去商店,购买足够的纸来制作第一个源阵列的单个副本。 然后,手动将源数组复制到新纸上,确保填充任何空白的稀疏点。 然后,返回商店,为第二个源阵列购买足够的纸张。 然后,遍历第二个源数组并复制它,同时确保副本中没有空白。 然后,将所有复印的纸张装订在一起。

在上面的类比中,选项 #1 代表 Array.prototype.concat 而 #2 代表 Array.prototype.push.apply。 让我们用一个类似的 JSperf 测试一下,不同之处仅在于它测试稀疏数组上的方法,而不是实体数组。 人们可以在这里找到它。

因此,我认为这个特定用例的性能未来不在于 Array.prototype.push,而在于 Array.prototype.concat。

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

𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀

当数组的某些成员完全丢失时。 例如:

 // 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]);

或者,javascript 允许您轻松初始化备用阵列。

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

𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀

类数组是至少具有length属性的对象,但未使用new Array[]初始化; 例如,以下对象被归类为类数组。

{0: "foo", 1: "bar", length:2}
document.body.children
new Uint8Array(3)
  • 这是类数组,因为尽管它是一个(n)(类型化)数组,但将其强制转换为数组会更改构造函数。
(function(){return arguments})()

观察使用将类似数组的内容强制转换为类似切片的数组的方法会发生什么。

 var slice = Array.prototype.slice; // For arrays: console.log(slice.call(["not an array-like, rather a real array"])); // For array-likes: console.log(slice.call({0: "foo", 1: "bar", length:2})); console.log(slice.call(document.body.children)); console.log(slice.call(new Uint8Array(3))); console.log(slice.call( function(){return arguments}() ));

  • 注意:由于性能原因,在函数参数上调用 slice 是不好的做法。

注意使用强制阵列喜欢到像CONCAT阵列的方法会发生什么。

 var empty = []; // For arrays: console.log(empty.concat(["not an array-like, rather a real array"])); // For array-likes: console.log(empty.concat({0: "foo", 1: "bar", length:2})); console.log(empty.concat(document.body.children)); console.log(empty.concat(new Uint8Array(3))); console.log(empty.concat( function(){return arguments}() ));

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)

这能解决你的问题吗?

在 JavaScript ES6 中,您可以将 ... 运算符用作扩展运算符,该运算符基本上会将数组转换为值。 然后,您可以执行以下操作:

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

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

虽然语法简洁,但我不知道它在内部是如何工作的,以及对大型数组的性能影响是什么。

下面的函数没有数组长度问题,并且比所有建议的解决方案执行得更好:

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];
    }
}

不幸的是,jspref 拒绝接受我的提交,所以这里是使用 benchmark.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

在哪里

for 循环和 push 是:

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

推送应用:

target.push.apply(target, source);

展开运算符:

    target.push(...source);

最后“设置长度和循环”是上面的函数

这是 ES6 的方式

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

上述方法适用于大多数情况,而不适用的情况请考虑concat ,例如数组中有数十万个项目。

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

如果要修改原始数组,可以进行传播和推送:

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

如果您想确保source数组中只有相同类型的项目(例如,不混合数字和字符串),请使用 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);
}

有很多关于Array.prototype.push.apply的答案。 这是一个明显的例子:

 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]

如果你有 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]

我提供了两种简单的方法来做到这一点:

解决方案 1:

let dataArray1= [0, 1];
let dataArray1= [2, 3];
dataArray1.push(...dataArray2);
console.log(dataArray1) 

解决方案 2:

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

我们有两个数组 a 和 b。 这里所做的代码是将数组 a 的值推送到数组 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' ]

尝试这个:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayB.reduce((pre, cur) => [...pre, ...cur], arrayA);
console.log(newArray)
    public static void main(String[] args) {
    // TODO Auto-generated method stub
    //Scanner sc=new Scanner(System.in);
    
    int[] ij= {1,4,222,455,111};
    int[] ijk=Arrays.copyOf(ij,ij.length);
    for(int i=0;i<ij.length;i++) {
        System.out.print(i);
    }
    System.out.println(" ");
    for(int i=0;i<ijk.length;i++) {
        System.out.print(i);
    }
    
    
}

极快

我分析了当前的解决方案并提出了 2 个新的(F 和 G 在细节部分介绍)一个对于中小型阵列非常快

表现

今天 2020.11.13 我在 Chrome v86、Safari v13.1.2 和 Firefox v82 上对 MacOs HighSierra 10.13.6 进行测试,以选择解决方案

结果

适用于所有浏览器

  • 对于中小型数组,基于while-pop-unshift (F,G) 的解决方案在所有浏览器上(非常)最快。 对于具有 50000 个元素的数组,此解决方案在 Chrome 上会减慢速度
  • 数组 500000 的解决方案 C、D 中断:“RangeError: Maximum call stack size exceeded
  • 解决方案(E)最慢

在此处输入图片说明

细节

我执行 2 个测试用例:

  • 当数组有 10 个元素时 - 你可以在这里运行它
  • 当数组有 10k 个元素时 - 你可以在这里运行它

下面的代码片段展示了解决方案ABCDE 、F(my)、G(my)、 HI之间的差异

 // https://stackoverflow.com/a/4156145/860099 function A(a,b) { return a.concat(b); } // https://stackoverflow.com/a/38107399/860099 function B(a,b) { return [...a, ...b]; } // https://stackoverflow.com/a/32511679/860099 function C(a,b) { return (a.push(...b), a); } // https://stackoverflow.com/a/4156156/860099 function D(a,b) { Array.prototype.push.apply(a, b); return a; } // https://stackoverflow.com/a/60276098/860099 function E(a,b) { return b.reduce((pre, cur) => [...pre, cur], a); } // my function F(a,b) { while(b.length) a.push(b.shift()); return a; } // my function G(a,b) { while(a.length) b.unshift(a.pop()); return b; } // https://stackoverflow.com/a/44087401/860099 function H(a, b) { var len = b.length; var start = a.length; a.length = start + len; for (var i = 0; i < len; i++ , start++) { a[start] = b[i]; } return a; } // https://stackoverflow.com/a/51860949/860099 function I(a, b){ var oneLen = a.length, twoLen = b.length; var newArr = [], newLen = newArr.length = oneLen + twoLen; for (var i=0, tmp=a[0]; i !== oneLen; ++i) { tmp = a[i]; if (tmp !== undefined || a.hasOwnProperty(i)) newArr[i] = tmp; } for (var two=0; i !== newLen; ++i, ++two) { tmp = b[two]; if (tmp !== undefined || b.hasOwnProperty(two)) newArr[i] = tmp; } return newArr; } // --------- // TEST // --------- let a1=[1,2,3]; let a2=[4,5,6]; [A,B,C,D,E,F,G,H,I].forEach(f=> { console.log(`${f.name}: ${f([...a1],[...a2])}`) })

以下是 chrome 的示例结果

在此处输入图片说明

而不是 push() 函数使用 IE 的 concat 函数。 例子,

var a=a.concat(a,new Array('amin'));

Тhis 是一个工作代码,它工作正常:

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))}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM