基于一个标识列值合并和分组包含对象的两个数组


Merging and group two arrays containing objects based on one identifying column value

我在按特定场景合并数组方面遇到了麻烦。搜索类似案例在这里没有结果。要清楚地了解我的要求是什么,请查看以下示例:

$array1 = [
    (object) ['call_date' => '2013-10-22 00:00:00', 'first_amount' => 10],
    (object) ['call_date' => '2013-10-23 00:00:00', 'first_amount' => 20],
];
$array2 = [
    (object) ['call_date' => '2013-10-22 00:00:00', 'second_amount' => 30],
    (object) ['call_date' => '2013-10-24 00:00:00', 'second_amount' => 40],
];

我需要什么作为输出:

Array
(
    [0] => stdClass
        (
            [call_date] => 2013-10-22 00:00:00
            [first_amount] => 10
            [second_amount] => 30
        )
    [1] => stdClass
        (
            [call_date] => 2013-10-23 00:00:00
            [first_amount] => 20
        )
    [2] => stdClass
        (
            [call_date] => 2013-10-24 00:00:00
            [second_amount] => 40
        )
)

如您所见,合并按call_date进行。合并日期下的第一个和第二个数组中的项目2013-10-22 00:00:00,附加日期2013-10-24 00:00:00下第二个数组中的项目。

尝试了很多array_merge、array_udiff、array_merge_recursive、array_map的组合,但没有任何帮助。

简单场景:

  1. 更改阵列键
  2. 合并递归
  3. 将合并的元素映射回对象 (stdClass)

例:

//change key
$workFirstArray = array_combine(
    array_map(function($object) { return $object->call_date;}, $firstArray), $firstArray
);
$workSecondArray = array_combine(
    array_map(function($object) { return $object->call_date;}, $secondArray), $secondArray
);
//map merged elements back to StdClass
$result = array_map(function($element) {
        if(is_array($element)) {
            $element['call_date'] = end($element['call_date']);
            $element=(object)$element;
        }
        return $element;
    },
    array_merge_recursive($workFirstArray, $workSecondArray)
);

输出:

Array
(
    [0] => stdClass Object
        (
            [call_date] => 2013-10-22 00:00:00
            [first_amount] => 10
            [second_amount] => 40
        )
    [1] => stdClass Object
        (
            [call_date] => 2013-10-23 00:00:00
            [second_amount] => 30
        )
    [2] => stdClass Object
        (
            [call_date] => 2013-10-24 00:00:00
            [second_amount] => 40
        )
)
$result = array();
$result = $firstArray;
for ($i=0; $i < count($firstArray); $i++){
    for ($j=0; $j < count($secondArray); $j++){
        if ($firstArray[$i]['call_date'] == $secondArray[$j]['call_date']){
            $result[$i]['second_amount'] = $secondArray[$j]['second_amount'];
            break;
        }
    }
    if ($j == count($secondArray))
        $result[] = $secondArray[$j-1];
}
var_dump($result);
$result = $first;
$amounts = array('first_amount','second_amount');
foreach ($first as $fKey => $f) {
    foreach ($second as $sKey => $s) {
        if ($f['call_date'] == $s['call_date']) {
            foreach ($amounts as $amount) {
                if (!isset($f[$amount]) && isset($s[$amount]))
                    $result[$fKey][$amount] = $s[$amount];
            }
            unset($second[$sKey]);
            break;
        }
    }
}
$result = array_merge($result, $second);

如果您有更多键添加金额数组,如果要替换现有键,请从条件中删除!isset($f[$amount]) &&,并且您有多个相等的call_date也删除break;

由于对象需要的处理方式与数组略有不同,因此不能简单地对对象使用数组合并技术。 在我看来,最好合并两个数组,然后迭代行/对象。

迭代时,生成一个新数组,其中组标识值用作第一级键。

对象

不需要暂时转换为数组,然后恢复到其初始对象类型 - 我发现这种方法是间接的,并且可能效率低下。

数组

和对象之间的另一个区别是,数组在使用方括号语法推送时不需要声明其父元素,但对于对象,您必须在将子对象推送到父对象之前声明父对象。

下面我使用空合并赋值运算符 ( ??= ) 作为仅在父对象尚未声明时声明空父对象的方法。

将每个对象的每个属性(键和值)推入其组。 完成后,您可以使用 array_values() 删除第一级关联键。

(下面两个代码段的演示)(或演示将??=替换为isset()条件)

函数式代码:

var_export(
    array_values(
        array_reduce(
            array_merge($array1, $array2),
            function($result, $obj) {
                $result[$obj->call_date] ??= (object) [];
                foreach ($obj as $prop => $val) {
                    $result[$obj->call_date]->$prop = $val;
                }
                return $result;
            }
        )
    )
);

或没有空合并赋值

var_export(
    array_values(
        array_reduce(
            array_merge($array1, $array2),
            function($result, $obj) {
                if (!isset($result[$obj->call_date])) {
                    $result[$obj->call_date] = $obj;
                } else {
                    foreach ($obj as $prop => $val) {
                        $result[$obj->call_date]->$prop = $val;
                    }
                }
                return $result;
            }
        )
    )
);

经典法:

$result = [];
foreach (array_merge($array1, $array2) as $obj) {
    $result[$obj->call_date] ??= (object) [];
    foreach ($obj as $prop => $val) {
        $result[$obj->call_date]->$prop = $val;
    }
}
var_export(array_values($result));

或没有空合并赋值

$result = [];
foreach (array_merge($array1, $array2) as $obj) {
    if (!isset($result[$obj->call_date])) {
        $result[$obj->call_date] = $obj;
    } else {
        foreach ($obj as $prop => $val) {
            $result[$obj->call_date]->$prop = $val;
        }
    }
}
var_export(array_values($result));

我不赞成在另一个数组的另一个循环中使用一个数组的循环,除非组标识值是用作数组键时会发生变化的数据类型。 (例如,浮点值在用作键时将作为整数下限)