Обединяване на два масива с комбиниране на дубликати по едно от полетата

Имам проблем с обединяването на масиви по конкретен сценарий. Търсенето на подобни случаи не даде резултат тук. За да разберете ясно какви са изискванията ми, моля, вижте следващия пример:

Първи масив:

Array
(
    [0] => stdClass
        (
            [call_date] => 2013-10-22 00:00:00
            [first_amount] => 10
        )

    [1] => stdClass
        (
            [call_date] => 2013-10-23 00:00:00
            [first_amount] => 20
        )
)

Втори масив:

Array
(
    [0] => stdClass
        (
            [call_date] => 2013-10-22 00:00:00
            [second_amount] => 30
        )

    [1] => stdClass
        (
            [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, но нищо не помогна :(.

Ще съм благодарен за решаването на този проблем!


person Riddick    schedule 29.10.2013    source източник
comment
A за всеки, с тестово условие? php.net/manual/en/control-structures.foreach.php . Всички тези функции са само foreach.   -  person Rémi Benoit    schedule 29.10.2013
comment
Функциите за масиви няма да са особено полезни тук – тъй като нямате имате масиви във вашите масиви, а обекти. Така че ще трябва да добавите атрибути към съществуващ обект или да създадете нов обект, независимо дали такъв с call_date вече съществува в първия ви масив.   -  person CBroe    schedule 29.10.2013
comment
Просто foreach, използвайки call_date като ключ на нов масив, ако даден елемент не съществува, добавете, ако съществува, добавете колоната *_amount (или просто $newarray[$item->call_date] = (object)array_merge(get_object_vars($newarray[$item->call_date]),get_object_vars($item)); (Говорите за сливане на обект тук.   -  person Wrikken    schedule 29.10.2013
comment
@Remi, благодаря за отговора. И така, кой е най-оптималния начин за използване на foreach? Трябва ли да използвам вложени foreach-s?   -  person Riddick    schedule 29.10.2013
comment
Не, само 1 foreach последователно за всеки масив, който искате да добавите към него. (така че foreach($array1 as $item){..}foreach($array2 as $item){..} и т.н.)   -  person Wrikken    schedule 29.10.2013
comment
@CBroe, мога да конвертирам обекти в масиви, това не е основният проблем, можете да разгледате масива вместо stdClass.   -  person Riddick    schedule 29.10.2013
comment
@Riddick Няма оптимален начин за такава основна езикова функция. Предложете някакъв код, тогава можем да говорим повече!   -  person Rémi Benoit    schedule 29.10.2013
comment
Е, тогава не виждам никакъв реален проблем с това... просто проверявате дали вече има елемент за call_date и добавяте новата стойност към него или създавате нов. Наистина трябва да опитате това сами, защото от сега вашият въпрос е просто да поискате код.   -  person CBroe    schedule 29.10.2013
comment
Какво ще кажете за: array_unique? array_unique($array1 + $array2 + $array3) php.net/manual /en/function.array-unique.php   -  person BENARD Patrick    schedule 29.10.2013
comment
@Jahnux73, вече опитах, не помогна, тъй като премахва само пълни дублирани записи.   -  person Riddick    schedule 29.10.2013


Отговори (4)


Прост сценарий:

  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
        )

)
person ziollek    schedule 29.10.2013
comment
УАУ, това проработи и то доста бързо! Благодаря ти много! Добавено е само едно нещо, за да завършите вашето решение: $result = array_values($result); - person Riddick; 29.10.2013

$arr1 = array(  array(  'call_date'    => '2013-10-22 00:00:00',
                        'first_amount' => 10),
                array(  'call_date'    => '2013-10-23 00:00:00',
                        'first_amount' => 20));

$arr2 = array(  array(  'call_date'     => '2013-10-22 00:00:00',
                        'second_amount' => 30),
                array(  'call_date'     => '2013-10-24 00:00:00',
                        'second_amount' => 40));

$arr_merged = array_merge($arr1, $arr2);

$arr_result = array();
foreach ($arr_merged as $arr) {

    if ( ! isset($arr_result[$arr['call_date']]))
        $arr_result[$arr['call_date']] = array();

    $arr_result[$arr['call_date']] += $arr;

}

$arr_result = array_values($arr_result);
  1. Дефинирайте подходящи масиви като $arr1 и $arr2
  2. array_merge() тях
  3. Създайте нов масив с логика на обединение на 'call_date'
  4. Премахнете датата като ключ чрез array_values()
person Robin Castlin    schedule 29.10.2013
comment
Не работи. За големи масиви има грешки: Notice: Undefined index: 2013-06-26 03:00:00 ... Fatal error: Unsupported operand types ... - person Riddick; 29.10.2013
comment
Опитайте сега. Може да се наложи първо да дефинирате стойност като array. - person Robin Castlin; 29.10.2013
comment
Да, сега работи. Малко по-бавно от решението на @ziollek (25-30 ms до 40-45 ms), но също работи. Това е алтернативното решение! Благодаря ти! - person Riddick; 29.10.2013

$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;.

person Anees Sadeek    schedule 29.10.2013

person    schedule
comment
Вашето решение очаква фиксирани индекси (като second_amount), но трябва да бъде независимо от тях. - person Riddick; 29.10.2013