分割数组的几种方法比较

需求:

前段时间遇到一个需求,批量同步数据。
给过来的数据如果本地已经有的就更新,没有的就创建。

分析

因为数据有差异,不能进行覆盖因此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 协议》,转载必须注明作者和本文链接
每天进步一点点,多年以后再回头,就会发现自己不知不觉走了很远很远
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 2

$origin_new = array_combine(array_column($origin, $key), $origin);

$origin_new = array_column($origin, null, $key);

5年前 评论

@lovecn 多谢指出,我确实没想到$origin_new = array_column($origin, null, $key);这个可以直接实现。 :kissing_heart:

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!