0

I have two separate arrays like the following:

$array = array(
  array("id" => "1", "name" => "name1"),
  array("id" => "2", "name" => "name2"),
  array("id" => "3", "name" => "name3"),
  array("id" => "4", "name" => "name4"),
  array("id" => "5", "name" => "name5"),
  array("id" => "6", "name" => "name6"),
  array("id" => "7", "name" => "name7"),
  array("id" => "8", "name" => "name8"),
  array("id" => "9", "name" => "name9"),
  array("id" => "10", "name" => "name10"),
  array("id" => "11", "name" => "name11"),
  array("id" => "12", "name" => "name12"),
);

$array1 = array(
  array("id" => "1", "description" => "description1"),
  array("id" => "2", "description" => "description2"),
  array("id" => "3", "description" => "description3"),
  array("id" => "4", "description" => "description4"),
  array("id" => "5", "description" => "description5"),
  array("id" => "6", "description" => "description6"),
  array("id" => "7", "description" => "description7"),
  array("id" => "8", "description" => "description8"),
  array("id" => "9", "description" => "description9"),
  array("id" => "10", "description" => "description10"),
  array("id" => "11", "description" => "description11"),
  array("id" => "12", "description" => "description12"),
);

I want to compare and match the name and description of the two arrays based on the id number. I came up with the following code:

foreach($array as $value){
      foreach($array1 as $value1){
        if($value['id'] == $value1['id']){
          echo "name is ".$value['name']. " and description is ".$value1['description']."<p>";

          }
        }
  }

That displays the outcome:

name is name1 and description is description1

name is name2 and description is description2

name is name3 and description is description3

name is name4 and description is description4

name is name5 and description is description5

name is name6 and description is description6

name is name7 and description is description7

name is name8 and description is description8

name is name9 and description is description9

name is name10 and description is description10

name is name11 and description is description11

name is name12 and description is description12

This is exactly what I wanted but I was wondering if there is a way to decrease the runtime of the code since I used foreach twice, it will need to go through each array to check if the id number match. I will have an array that will have over 100 arrays in it and I will need to compare two arrays to find matching values. Therefore it might be slow. Is there a quicker way other than foreach to compare two arrays?

MasterA
  • 13
  • 1
  • 4
  • 1
    As more general advise: I recommend running a test with meaningful datasets and determine that your current implementation is too slow before optimizing a perceived bottleneck. Don't optimize based on a feeling but use a data-driven approach. It's also great to be able to state by how much you increased performance with clear numbers. – k0pernikus Sep 19 '18 at 16:29
  • I agree with @k0pernikus, while the below answers are good and give you a cleaner code. If your aim is optimizations you should run tests to prove its efficiency. 'Cause internal function, even though fast, might cost you in performance and eventually give the same results in performance as compared to the answer presented by you. An alternative would be to cache your result so that the loop need not run on every request cycle (depending on the use case). – Shalom Sam Sep 19 '18 at 16:39

5 Answers5

1

The simplest way to combine the two arrays into one would be a couple of calls to array_column to re-index them by the ID field, and then a call to array_replace_recursive to combine the two together:

$array = array_column($array, null, 'id');
$array1 = array_column($array1, null, 'id');

$merged = array_replace_recursive($array, $array1);

Then you can simply loop over the rows, and just deal with the presentation of a single item at a time:

foreach ($merged as $row) {
  echo "name is {$row['name']} and description is {$row['description']}", PHP_EOL;
}

name is name1 and description is description1
...

See https://eval.in/1058846

iainn
  • 16,826
  • 9
  • 33
  • 40
  • It's a good answer, but it will fail on duplicate IDs. For [example](http://sandbox.onlinephpfunctions.com/code/36ba1378e8e65160a54be11be7ed735cefc812a2). The last item in the second array is a duplicate (which I added in). That said it's not clear if that would be an issue or not. – ArtisticPhoenix Sep 19 '18 at 16:50
0

Not really, unless

  1. You know that the two arrays contain the same ids in the same order. Then you can use the key from one array to refer to the other.

    foreach ($array as $key => $value) {
        echo "name is $value[name] and description is $value1[$key][description]<p>";
    }
    
  2. You can somehow index both of the arrays using the id as the key. Depending on how you get them, you may be able to construct them this way. For example, use that field as a key when fetching rows from a database result into an array.

    while($row = $result->fetch()) {
        $array[$row['id']] = $row;
    }
    // then you can use the same foreach as above even if the results
    // aren't in the same order.
    

    (If the arrays really do come from a database, you should consider using a join and getting them with one query.)

Other than that, one thing that will speed up the nested foreach you're currently using is if you break out of the inner loop after you find a match.

Don't Panic
  • 41,125
  • 10
  • 61
  • 80
0

Instead of having a multiple of the two arrays, just foreach each array once and merge them. It all depends on what data you get and what data you want to have.

$array = array(
    array("id" => "1", "name" => "name1"),
    array("id" => "2", "name" => "name2"),
    array("id" => "3", "name" => "name3"),
    array("id" => "4", "name" => "name4"),
    array("id" => "5", "name" => "name5"),
    array("id" => "6", "name" => "name6"),
    array("id" => "7", "name" => "name7"),
    array("id" => "8", "name" => "name8"),
    array("id" => "9", "name" => "name9"),
    array("id" => "10", "name" => "name10"),
    array("id" => "11", "name" => "name11"),
    array("id" => "12", "name" => "name12"),
);

$array1 = array(
    array("id" => "1", "description" => "description1"),
    array("id" => "2", "description" => "description2"),
    array("id" => "3", "description" => "description3"),
    array("id" => "4", "description" => "description4"),
    array("id" => "5", "description" => "description5"),
    array("id" => "6", "description" => "description6"),
    array("id" => "7", "description" => "description7"),
    array("id" => "8", "description" => "description8"),
    array("id" => "9", "description" => "description9"),
    array("id" => "10", "description" => "description10"),
    array("id" => "11", "description" => "description11"),
    array("id" => "12", "description" => "description12"),
);

$mergedArray = [];
foreach ($array as $row) {
    $mergedArray[$row["id"]] = $row;
}
foreach ($array1 as $row) {
    $mergedArray[$row["id"]]["description"] = $row["description"];
}
var_dump($mergedArray);

Output

array(12) {
  [1]=>
  array(3) {
    ["id"]=>
    string(1) "1"
    ["name"]=>
    string(5) "name1"
    ["description"]=>
    string(12) "description1"
  }
  [2]=>
  array(3) {
    ["id"]=>
    string(1) "2"
    ["name"]=>
    string(5) "name2"
    ["description"]=>
    string(12) "description2"
  }
  [3]=>
  array(3) {
    ["id"]=>
    string(1) "3"
    ["name"]=>
    string(5) "name3"
    ["description"]=>
    string(12) "description3"
  }
  [4]=>
  array(3) {
    ["id"]=>
    string(1) "4"
    ["name"]=>
    string(5) "name4"
    ["description"]=>
    string(12) "description4"
  }
  [5]=>
  array(3) {
    ["id"]=>
    string(1) "5"
    ["name"]=>
    string(5) "name5"
    ["description"]=>
    string(12) "description5"
  }
  [6]=>
  array(3) {
    ["id"]=>
    string(1) "6"
    ["name"]=>
    string(5) "name6"
    ["description"]=>
    string(12) "description6"
  }
  [7]=>
  array(3) {
    ["id"]=>
    string(1) "7"
    ["name"]=>
    string(5) "name7"
    ["description"]=>
    string(12) "description7"
  }
  [8]=>
  array(3) {
    ["id"]=>
    string(1) "8"
    ["name"]=>
    string(5) "name8"
    ["description"]=>
    string(12) "description8"
  }
  [9]=>
  array(3) {
    ["id"]=>
    string(1) "9"
    ["name"]=>
    string(5) "name9"
    ["description"]=>
    string(12) "description9"
  }
  [10]=>
  array(3) {
    ["id"]=>
    string(2) "10"
    ["name"]=>
    string(6) "name10"
    ["description"]=>
    string(13) "description10"
  }
  [11]=>
  array(3) {
    ["id"]=>
    string(2) "11"
    ["name"]=>
    string(6) "name11"
    ["description"]=>
    string(13) "description11"
  }
  [12]=>
  array(3) {
    ["id"]=>
    string(2) "12"
    ["name"]=>
    string(6) "name12"
    ["description"]=>
    string(13) "description12"
  }
}
Brain Foo Long
  • 2,057
  • 23
  • 28
0

You can reduce the lookup on the second array by doing this

$array = array(
  array("id" => "1", "name" => "name1"),
  array("id" => "2", "name" => "name2"),
  array("id" => "3", "name" => "name3"),
  array("id" => "4", "name" => "name4"),
  array("id" => "5", "name" => "name5"),
  array("id" => "6", "name" => "name6"),
  array("id" => "7", "name" => "name7"),
  array("id" => "8", "name" => "name8"),
  array("id" => "9", "name" => "name9"),
  array("id" => "10", "name" => "name10"),
  array("id" => "11", "name" => "name11"),
  array("id" => "12", "name" => "name12"),
);

$array1 = array(
  array("id" => "1", "description" => "description1"),
  array("id" => "2", "description" => "description2"),
  array("id" => "3", "description" => "description3"),
  array("id" => "4", "description" => "description4"),
  array("id" => "5", "description" => "description5"),
  array("id" => "6", "description" => "description6"),
  array("id" => "7", "description" => "description7"),
  array("id" => "8", "description" => "description8"),
  array("id" => "9", "description" => "description9"),
  array("id" => "10", "description" => "description10"),
  array("id" => "11", "description" => "description11"),
  array("id" => "12", "description" => "description12"),
);

$ids = array_column($array1, 'id');

foreach($array as $value){
    $index = array_search($value['id'], $ids);
    if(false !== $index){
        $value1 = $array1[$index];
        echo "name is ".$value['name']. " and description is ".$value1['description']."<p>";
    }
}

Here we are using array_column to get an array of just the ids from the second array. Because the $ids array is derived from the other array $array1 all of the numeric keys are correlated between the two.

Then we can easily search $ids with the id from the current item in the first array to find that index and use it to pull only the row we want from the second array.

This eliminates the need to modify the array structures, and simplifies things greatly. However, if you have duplicate ID's in a single array you will have problems. Which probably isn't an issue if they come from a properly indexed database.

The main thing is it eliminates the need for a second foreach that iterates over mostly useless items. You can even error check the second array if an id is missing by adding and else to the if I put in there.

Sandbox

Output

name is name1 and description is description1
name is name2 and description is description2
name is name3 and description is description3
name is name4 and description is description4
name is name5 and description is description5
name is name6 and description is description6
name is name7 and description is description7
name is name8 and description is description8
name is name9 and description is description9
name is name10 and description is description10
name is name11 and description is description11
name is name12 and description is description12

I should mention this is pretty much broken as it's invalid HTML (you'll have 12 open P tags an no closing in the above)

echo "name is ".$value['name']. " and description is ".$value1['description']."<p>";

IF you have duplicate IDs

Last if you can have duplicate ID's, you can use array_intersect on the the column instead and then iterate over the intersection like this:

foreach($array as $value){
    $intersect = array_intersect($ids, [$value['id']]);
    foreach($intersect as $index=>$id){
       $value1 = $array1[$index];
       if(false !== $index){
          $value1 = $array1[$index];
          echo "name is ".$value['name']. " and description is ".$value1['description']."<p>";
       }
    }
}

Sandbox

Which is still much less iterations then the original code does.

ArtisticPhoenix
  • 21,464
  • 2
  • 24
  • 38
0

You can use continue if you only expect one result, so that when you find a match it will not loop over the other entries.

Given two arrays with 10,000 entries your approach will take about 5 seconds whereas this code takes about 1.4s.

If you have a unixoid OS you can measure the execution time via:

 time php myScript.php

Or you may use microtime

<?php
$range = range(1, 1e4, 1);

$array = [];
$array1 = [];

foreach ($range as $i) {
    $array[] = [
        "id" => $i,
        "name" => "name ${i}"
    ];

    $array1[] = [
        "id" => $i,
        "description" => "description ${i}"
    ];
}

$result = [];
foreach ($array as $value) {
    foreach ($array1 as $value1) {
        if ($value["id"] == $value1["id"]) {
            $result[] = "name is " . $value['name'] . " and description is " . $value1['description'] . "<p>";
            continue 2;
        }
    }
}

echo implode("\n", $result);

If raw speed was the main goal, you can echo within the nested foreach loop. I prefer to separate data processing from its visualization even if it means looping over the data again. (A classic for-loop is rather cheap and only for really big array sizes this will be of concern.)

k0pernikus
  • 60,309
  • 67
  • 216
  • 347