1. 程式人生 > >比較JS合併陣列的各種方法及其優劣

比較JS合併陣列的各種方法及其優劣

原文連結:  Combining JS Arrays
原文日期: 2014-09-09
翻譯日期: 2014-09-18
翻譯人員: 鐵錨

本文屬於JavaScript的基礎技能. 我們將學習結合/合併兩個JS陣列的各種常用方法,並比較各種方法的優缺點. 

我們先來看看具體的場景:
var q = [ 5, 5, 1, 9, 9, 6, 4, 5, 8];
var b = [ "tie", "mao", "csdn", "ren", "fu", "fei" ];

很明顯,陣列 q 和 b 簡單拼接的結果是:
[
    5, 5, 1, 9, 9, 6, 4, 5, 8, 
    "tie", "mao", "csdn", "ren", "fu", "fei"
]

concat(..)方法

最常見的用法如下:
var c = q.concat( b );

q; // [5,5,1,9,9,6,4,5,8]
b; // ["tie","mao","csdn","ren","fu","fei"];

c; // [5,5,1,9,9,6,4,5,8,"tie","mao","csdn","ren","fu","fei"]
如您所見, c 是一個全新的陣列, 表示 qb 這兩個陣列的組合, 但是 qb 現在沒用了是吧?
如果 q 陣列有10000個元素, b 陣列也有有10000個元素? 那麼陣列c現在就有20000個元素, 這種方式佔用了2倍的記憶體. 
“這沒問題!”,你可能會覺得. 只要將 q 和 b 置空就行, 然後就會被垃圾回收,對嗎?問題解決了!
q = b = null; // `q` and `b` 現在可以被垃圾回收了
額? 如果陣列都很小,那自然沒問題. 但對大型的陣列,或需要多次重複處理時, 記憶體就被限制了, 它還需要進行優化. 

迴圈插入

OK, 讓我們把一個數組的內容加入到另一箇中試試,使用 Array#push() 方法:
// 將陣列 `b` 插入 `q`
for (var i=0; i < b.length; i++) {
    q.push( b[i] );
}

q; // [5,5,1,9,9,6,4,5,8,"tie","mao","csdn","ren","fu","fei"]

b = null;

現在, q中存放了兩個原始陣列的內容(q + b). 
看樣子對記憶體優化做的不錯. 
但如果 q
陣列很小而 b 又很大呢? 出於記憶體和速度的考慮,這時想把較小的 q 插入到 b 前面. 沒問題,只要用 unshift() 方法代替 push() 即可, 對應的也要從大到小進行迴圈遍歷:
// `q` into `b`:
for (var i=q.length-1; i >= 0; i--) {
    b.unshift( q[i] );
}

b; // [5,5,1,9,9,6,4,5,8,"tie","mao","csdn","ren","fu","fei"]

q = null;

實用技巧

悲催的是,for迴圈很土並且難以維護. 我們能做得更好嗎?
我們先試試 Array#reduce :
// `b` onto `q`:
q = b.reduce( function(coll,item){
    coll.push( item );
    return coll;
}, q );

q; // [5,5,1,9,9,6,4,5,8,"tie","mao","csdn","ren","fu","fei"]

// or `q` into `b`:
b = q.reduceRight( function(coll,item){
    coll.unshift( item );
    return coll;
}, b );

b; // [5,5,1,9,9,6,4,5,8,"tie","mao","csdn","ren","fu","fei"]

Array#reduce()Array#reduceRight() 很高大上,但有點笨重,而且一般人也記不住.  JS規範6 中的 => 箭頭函式(arrow-functions) 能讓程式碼量大大減少, 但需要對每個陣列元素執行函式呼叫, 也是很渣的手段. 
那麼下面的程式碼怎麼樣呢?
// `b` onto `q`:
q.push.apply( q, b );

q; // [5,5,1,9,9,6,4,5,8,"tie","mao","csdn","ren","fu","fei"]

// or `q` into `b`:
b.unshift.apply( b, q );

b; // [5,5,1,9,9,6,4,5,8,"tie","mao","csdn","ren","fu","fei"]

BIG更高了,是吧!? 特別是 unshift() 方法不需要像前面那樣考慮相反的順序. ES6 的展開運算子(spread operator, 加 ... 字首)就更高端了: a.push( ...b ) 或者 b.unshift( ...a )
但是,事實上這種方法還是太樂觀了. 在這兩種情況下,不管是將 a 或 b 傳遞給 apply() 作為第二個引數(apply方式呼叫Function時第一個引數在內部變成this,即context,上下文,作用域), 還是使用 ... 展開運算子的方式, 實際上陣列都會被打散成為函式的 arguments .
第一個主要的問題是,佔用了雙倍的記憶體(當然,是臨時的!),因為需要將陣列複製到函式棧之中. 此外,不同的JS引擎有不同的實現演算法,可能會限制了函式可以傳遞的引數數量. 
如果陣列添加了一百萬個元素, 那一定會超過函式棧所允許的大小, 不管是push()unshift()呼叫. 這種方式只在幾千個元素時可用,所以必須限制其不能超過一定範圍. 

注意: 你也可以試試 splice(), 肯定會發現他和 push(..)/unshift(..) 都是一樣的限制.

一種選擇是繼續使用這種方法,但是採用分批次處理:
function combineInto(q,b) {
    var len = q.length;
    for (var i=0; i < len; i=i+5000) {
        // 一次處理5000條
        b.unshift.apply( b, q.slice( i, i+5000 ) );
    }
}

等等,我們損害了程式碼的可讀性(甚至是效能!). 在我們放棄之前結束這個旅程吧. 

總結

Array#concat()
是久經考驗的方法, 用於組合兩個(或多個)陣列. 但他建立了一個新的陣列,而不是修改現有的一個. 
有很多變通的手法,但他們都有不同的優缺點,需要根據實際情況來選擇. 
上面列出了各種 優點/缺點,也許最好的(包括沒有列出的)方法是 reduce(..)reduceRight(..) 
無論你選擇什麼,都應該批判性地思考你的數組合並策略,而不是把它當作理所當然的事情. 

相關推薦

比較JS合併陣列各種方法及其優劣

原文連結:  Combining JS Arrays原文日期: 2014-09-09翻譯日期: 2014-09-18翻譯人員: 鐵錨本文屬於JavaScript的基礎技能. 我們將學習結合/合併兩個JS陣列的各種常用方法,並比較各種方法的優缺點. 我們先來看看具體的場景:va

js 合併陣列的一種方法

function a(first, second) { var len = +second.length, j = 0, i = first.length; console.log(len); for (; j < len; j++) { //很巧妙的在

一些常用 js 對於 陣列 字串 方法總結

一 、陣列concat連線 var arrayA = [1,2,3] var arrayB = [4,5,6] var arrayC = [7,8,9] console.log(arrayA.concat(arrayB)) //(6) [1,2,3,4,5,6] console.log(

js陣列方法總結

js中陣列的方法很多,經常都翻書看,無奈老是記不住,一到用的時候就忘了,大概還是因為用的少,也可能是因為方法確實太多,據不完全統計有23種。。。所以在這裡總結一下,當是記個筆記。 1、join() join(separator): 將陣列的元素組起一個字串,以separator為分隔符,省略

原生js替換jQuery各種方法-中文版

You Don't Need jQuery 前端發展很快,現代瀏覽器原生 API 已經足夠好用。我們並不需要為了操作 DOM、Event 等再學習一下 jQuery 的 API。同時由於 React、Angular、Vue 等框架的流行,直接操作 DOM

js陣列方法

陣列的方法 push 作用:在陣列末尾追加元素。 引數:追加到陣列後面的元素,可以是多個,用逗號隔開。 返回值:新陣列的長度。 原有陣列變化 pop 作用:刪除陣列最後一個元素。 引數不需要引數。 返回值 :被刪除的陣

js 合併陣列

js concat是合併是返回一個新的陣列,並不是改變原來的陣列。這點小心 console.log(tmp.Table1) console.log(res.data.FObject.Table1)    var tmp = tmp.Table1.concat(res.dat

js 合併陣列(兩種方式)

let arr1=[1,2,3]; let arr2=[4,5,6]; console.log(arr1.concat(arr2) )  //[1,2,3,4,5,6]       concat() 方法用於連線兩個或多個數組。 該方法不會改變現有的陣列,而僅僅會

js刪除陣列元素方法 總結

方法一:delete arr[x] 用delete刪除後,陣列的長度length不會發生變化,此時arr[x]變為undefined。 好處:delete arr[x]後陣列的索引保持不變。 var arr = [1,true,{},"a"]; del

三種Fibonacci數列第n項計算方法及其優劣分析

感謝國防科技大學劉萬偉老師和中國傳媒大學胡鳳國兩位老師提供的思路,文章作者不能超過8個字元,我的名字就寫個姓吧,名字不寫了^_^。另外,除了本文討論的三種方法,之前的文章中還討論了另外幾種方法,詳見相關閱讀第一篇。 def fibo4(n):     '''遞推法     適用於任意大小的n     使用生

js陣列常用方法總結

  運算元組 運算元組,印象中運算元組的方法很多,下面總結了一下陣列中常用的幾個方法: JavaScript中建立陣列有兩種方式 (一)使用 Array 建構函式:   var arr1 = new Array(); //建立一個空陣列var arr2 = new Array(

js陣列常用方法的簡單重構(部分),幫助你加快理解陣列中常用方法

## `push` 將指定數字插入到陣列的末尾,返回值為`新陣列的長度` ```javascript //原方法示例: var ary = [1, 2, 3, 4, 5]; ary.length = 5; ary.push(1) ; ary = [1,2,3,4,5,1

js陣列遍歷的幾種方法及其區別

第一種最常用的:for迴圈  for(j = 0; j < arr.length; j++) { }  優化版for迴圈   for(j = 0,len=arr.length; j < len; j++) {

JS 陣列陣列物件的合併去重方法

這次對陣列的操作,我們使用es6的新語法來完成,所以在看下邊程式碼之前,我們要了解我們下邊要用到的es6中的set集合,和for...of 方法: 首先介紹set集合: ES6提供了新的資料結構Set,它類似於陣列,但是成員的值都是唯一的,沒有重複的值。 Set函式可以接受一個數組(

js比較兩個陣列中是否含有相同的元素,可去重,可刪除合併為新陣列

//做比較的兩個陣列 var array1 = ['a','b','c','d','e'];//陣列1 (所有人) var array2 = ['d','f','e','a','p'];//陣列2 (需要刪除的人) //臨時陣列存放 var tempArray1 = [];//臨時

Python 合併兩個字典的各種方法以及效率的比較

下面的程式碼舉例了5種合併兩個字典的方法,並且做了個簡單的效能測試 #!/usr/bin/python import time def f1(d1, d2): return dict(d1, **d2) def f2(d1, d2):

js中Window 對象及其方法

tor size 種類 ear 無法 item back 瀏覽器中 取消 window.location 對象   window.location 對象用於獲得當前頁面的地址 (URL),並把瀏覽器重定向到新的頁面。window.location 對象在編寫時可不使用 wi

js數組的各種方法

檢測 spa 執行 字符 一個 ceo black ray 只有一個 1.檢測數組 ①Instanceof: if(value instanceof Array){ } 它假定只有一個全局執行環境,若網頁中包含多個框架,則存在多個不同的全局執行環境,則Instanceo

JS陣列,string類的定義及基本方法

函式: 函式在呼叫的時候,會形成一個私有作用域,內部的變數不會被外面訪問,這種保護機制叫閉包。這就意味著函式呼叫完畢,這個函式形成的棧記憶體會被銷燬。 function fn(){ var a=12; a++; console.log(a) } fn()----13 fn()----13 fn()---

JS 比較兩個陣列是否相等 是否擁有相同元素

Javascript怎麼比較兩個陣列是否相同? JS怎麼比較兩個陣列是否有完全相同的元素? Javascript不能直接用==或者===來判斷兩個陣列是否相等,無論是相等還是全等都不行,以下兩行JS程式碼都會返回false <script type="text/javascript">