Join arrays by equal keys, but separating values

Asked

Viewed 465 times

4

Scenario (example):

I have 2 Arrays, X and Y (dynamically created, individual):

$arX = Array('AAA' => 173.696, 'BBB' => 72.436, 'CCC' => 142.692);
$arY = Array('AAA' => 127, 'DDD' => 72.333);


Goal:

What I’d like is unite them by the key, but separate the values.

Example of the expected outcome:

Array
    (
        [AAA] => Array
        (
            [X] => 173.696,
            [Y] => 127
        )
        [BBB] => Array
        (
            [X] => 72.436
        )
        [CCC] => Array
        (
            [X] => 142.692
        )
        [DDD] => Array
        (
            [Y] => 72.333
        )
    )


Doubts:

  • There is a native function for this?
  • How could I get the result?
  • Question of the hour +1. Great challenge :p

  • Man, it’s okay to think about the rush I’m in. Then I’ll give you a better answer.

3 answers

2

You can use the function array_walk to accomplish what you need.

The central point, is to reference in callback what is the new array you will create ($newArray) and, as the third parameter of array_walk, the key preface (which can be X, Y or any other key you want, if there are more arrays).

$arX = Array('AAA' => 173.696, 'BBB' => 72.436, 'CCC' => 142.692);
$arY = Array('AAA' => 127, 'DDD' => 72.333);
$newArray = [];

$callback = function($value, $key , $prefix) use (&$newArray)
{
    //valida se a chave já existe no array
    if (!array_key_exists($key , $newArray))
    {
        $newArray[$key] = [];
    }

    // Adiciona o novo array ao já existente, utilizando o prefixo enviado via parâmetro.
    $newArray[$key] += [$prefix => $value];
};

//Cada array será processado individualmente e adicionado em $newArray
//que é referenciado no callback.
array_walk($arX , $callback , "X");
array_walk($arY , $callback , "Y");

Upshot:

array(4) {
  ["AAA"]=>
  array(2) {
    ["X"]=>
    float(173.696)
    ["Y"]=>
    int(127)
  }
  ["BBB"]=>
  array(1) {
    ["X"]=>
    float(72.436)
  }
  ["CCC"]=>
  array(1) {
    ["X"]=>
    float(142.692)
  }
  ["DDD"]=>
  array(1) {
    ["Y"]=>
    float(72.333)
  }
}

Code in operation: https://3v4l.org/Xmlv4

  • isset may fail if the value is null.

  • @Guilhermenascimento yes, but, ai depends on the need of the author of the question. I changed to array_key_exists

1

I had tried to give a more elaborate answer, using sophisticated array functions, but nothing was easier than using the array itself foreach.

Behold:

$result = [];

foreach ($arX as $key => $value)
{
    $result[$key]['X'] = $value;
}

foreach ($arY as $key => $value)
{
    $result[$key]['Y'] = $value;
}


var_dump($result);

Upshot:

[
 "AAA" => [
   "X" => 173.696,
   "Y" => 127,
 ],
 "BBB" => [
   "X" => 72.436,
 ],
 "CCC" => [
   "X" => 142.692,
 ],
 "DDD" => [
   "Y" => 72.333,
 ],
]

If you still want to insist on using functions to do this, you can combine the power of array_merge_recursive with array_map.

see:

$result = array_merge_recursive(
    array_map(function ($x) { return ['X' => $x]; }, $arX),
    array_map(function ($y) { return ['Y' => $y]; }, $arY)
);

In a third option, you could use the + operator along with array_keys to iterate with all keys of your array.

Behold:

$result = [];

foreach (array_keys($arX + $arY) as $key) {

    if (isset($arX[$key]))
        $result[$key]['X'] = $arX[$key];

    if (isset($arY[$key]))
        $result[$key]['Y'] = $arY[$key];
}
  • I liked this last option! It was very clean. You should change the isset for array_key_exists?

  • @Rbz depends on, the only problem of isset if the key exists, then NULL, he returns FALSE, but I believe your case makes no difference.

1


A single foreach with array_key_exists would already solve the problem, because this way will only iterate once, example with a function (does not need to be a function if will use only once):

<?php

function mesclar(array $arrX, array $arrY)
{
    //Cria uma nova array
    $z = array();

    //Pega as chaves de ambas arrays
    $chaves = array_merge( array_keys($arrX), array_keys($arrY) );

    foreach ($chaves as $chave)
    {
        // A performance do `=== false` é minima ou nenhuma na maioria dos casos, mas se o array for gigante mesmo talvez isto torne o script razoavelmente mais performatico
        if (isset($z[$chave]) === false) {
            $z[$chave] = array();
        }

        //Copia o valor do primeiro array para X
        if (array_key_exists($chave, $arrX))
        {
            $z[$chave]['X'] = $arrX[ $chave ];
        }

        //Copia o valor do primeiro array para Y
        if (array_key_exists($chave, $arrY))
        {
            $z[$chave]['Y'] = $arrY[ $chave ];
        }
    }

    return $z;
}

$arX = array('AAA' => 173.696, 'BBB' => 72.436, 'CCC' => 142.692);
$arY = array('AAA' => 127, 'DDD' => 72.333);

$novaArray = mesclar($arX, $arY);

print_r( $novaArray );

Example in IDEONE: https://ideone.com/3loYc9

  • You’re not bringing the "DDD".

  • @Rbz is absolutely right :/ I’ll review

  • @Revised Rbz https://ideone.com/3loYc9

  • I don’t know if it changes anything, but like this if (array_key_exists($chave, $z)) works as well. It would improve the "problem" you said "The performance of === false......" ?

  • @Rbz has nothing to check on $z, it is a totally new array that is created after passing the values. The === false will only have difference in giant arrays, usually even in giant array the gain is little, but I created the script without knowing which scenario you will need, ie a script that works well for large and small processes.

  • Excuse my confusion... but the isset is not checking if $z has that key, just like the array_key_exists?

  • 1

    @Rbz isset and array_keys_exists are similar, but not identical, it’s like I said in the beginning, I have no idea how many values your array will have and even less if the values will always be numbers, if a NULL appears in the middle of the values the isset would ignore the specific key, so I made the fault script in all possible scenarios (because I have no idea of its actual application) - See this answer: https://answall.com/a/117504/3635

Show 2 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.