分割陣列的幾種方法比較

一念滄海一念桑田發表於2019-03-12

需求:

前段時間遇到一個需求,批次同步資料。
給過來的資料如果本地已經有的就更新,沒有的就建立。

分析

因為資料有差異,不能進行覆蓋因此mysql的 REPLACE INTO 是不考慮了,
這時候,我需要把傳過來的資料進行分離,分離成兩個組,一個更新組,一個插入組。

首先來構建一下假資料

//源資料
$origin =[];
//當前本地資料
$current=[];
//生成10000個資料
for($i=1;$i<10000;$i++) {
    $origin[] =  ['id' => $i, 'title' => 'title'.$i, 'content' => 'name'.$i];
}
//假設本地的資料已經取好了,我用title來作為唯一值來處理吧,容易認一點
for($i=5000;$i<8000;$i++) {
    $current[] = 'title'.$i;
}

第一種使用foreach遍歷

$t1 = microtime(true);
$insertArr = [];
$updateArr = [];
foreach ($origin as $k=>$value) {
    if (!in_array($value['title'], $current)) {
        $insertArr[] = $value;
    }else {
        $updateArr[] = $value;
    }
}


$t2 = microtime(true);
dd($t2 - $t1);

程式碼執行時間:0.10883212089539

第二種使用array_map或array_walk進行遍歷

$t1 = microtime(true);
array_walk($origin, function($value) use ($current, &$insertArr, &$updateArr) {
    if (!in_array($value['title'], $current)) {
        $insertArr[] = $value;
    } else {
        $updateArr[] = $value;
    }
});
或者
array_map(function($value) use ($current, &$insertArr, &$updateArr) {
    if (!in_array($value['title'], $current)) {
        $insertArr[] = $value;
    } else {
        $updateArr[] = $value;
    }
}, $origin);
$t2 = microtime(true);
dd($t2 - $t1);

兩個執行時間差不多都是這個:0.11882400512695

第三種:array_diff_key交集比較

$t1 = microtime(true);

//把title取出來做為鍵
//$origin_new = array_combine(array_column($origin, 'title'), $origin);
$origin_new = array_column($origin, null, 'title');
//把值作為鍵
$current_new = array_flip($current);

//需要新增插入的資料 (用鍵來做差集比較得出新的源)
$insertArr = array_diff_key($origin_new, $current_new);

//需要進行更新的資料
$updateArr = array_diff_key($origin_new, $new_origin);


$t2 = microtime(true);
dd($t2 - $t1);

程式碼執行時間:0.0013279914855957

以上程式碼執行後分離的陣列都得到以下相同的資料

//列印陣列
dd($insertArr, $updateArr);

分離出的需要插入的陣列

array:7000 [
  "title1" => array:3 ["id" => 1
        "title" => "title1"
        "content" => "name1"
  ]
  "title2" => array:3 []
  "title3" => array:3 []
  ...
  "title4999" => array:3 [3]
  "title8000" => array:3 [3]
  "title8001" => array:3 [3]
  ...
  "title9999" => array:3 [3]
  "title10000" => array:3 [3]
]

分離出來的需要更新的資料

array:3000 ["title5000" => array:3 ["id" => 5000
         "title" => "title5000"
         "content" => "name5000"
  ]
  "title5001" => array:3 []
  "title5002" => array:3 []
  ...
  "title7998" => array:3 [3]
  "title7999" => array:3 [3]
]

總結

foreach、 array_map、 array_walk 這些需要遍歷的都比不過透過 array_diff_key 陣列鍵去對比取交集的方法。

選擇 array_diff_key 效率是最快的

以下進行封裝:

/**
 * 對比法分割陣列
 *
 * @param $key    透過指定鍵(key)去對比
 * @param $origin 原陣列
 * @param $split 分割陣列 (源陣列中的某個鍵的值)
 * @return array ['split'=>被分割出來的陣列, 'remainders'=>剩餘的資料];
 */
function splitArr($key, $origin, $split)
{
    //把title取出來做為鍵
    //$origin_new = array_combine(array_column($origin, $key), $origin);
    $origin_new = array_column($origin, null, $key);
    //把值作為鍵
    $split_flip = array_flip($split);

    //剩餘的源資料 (用鍵來做差集比較得剩餘的源資料)
    $remainders = array_diff_key($origin_new, $split_flip);
    //被分割出來的資料
    $new_split = array_diff_key($origin_new, $remainders);

    return ['split'=>$new_split, 'remainders'=>$remainders];
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結
每天進步一點點,多年以後再回頭,就會發現自己不知不覺走了很遠很遠

相關文章