T
T
topbanana2013-04-15 17:20:41
PHP
topbanana, 2013-04-15 17:20:41

How to avoid a nested loop and do it in a humane way?

Good afternoon!
I rack my brains, leafing through the function reference, I can’t think of anything better ...

There are 2 arrays: the original one is $data, and the array of new data is $new_data.
The goal is, if the values ​​of the two keys match, to replace the old data with the new ones.
You also need to know which elements of the new data did not match in the original.
There are more keys in the real array of initial data.

An easy way is nested loops. But, both arrays can have 1000 elements each ... My poor knowledge is not enough to do it humanly. Tell me how to optimize it?

//исходные данные
$data[] = array("a" => 'art1', "b" => '10');
$data[] = array("a" => 'art2', "b" => '20');
$data[] = array("a" => 'art3', "b" => '30');
$data[] = array("a" => 'art4', "b" => '40');
$data[] = array("a" => 'art5', "b" => '50');
$data[] = array("a" => 'art6', "b" => '60');

//новые данные
$new_data[] = array("a" => 'art1', "b" => '15');
$new_data[] = array("a" => 'art2', "b" => '25');
$new_data[] = array("a" => 'art3', "b" => '35');

foreach($data as $index => $data_val)		//для каждого элемента исходных данных
  foreach($new_data as $nk => $new_val)	//перебираем новые данные
    if($data_val["a"]===$new_val["a"]){	//если значения ключей совпали
      $data[$index]["b"]=$new_val["b"];	//меняем значение
      break;				//дальше не ищем
      }


print_r($data);
/*

Вот такой должен быть результат:

Array ( 
    [0] => Array ( [a] => art1 [b] => 15 ) 
    [1] => Array ( [a] => art2 [b] => 25 ) 
    [2] => Array ( [a] => art3 [b] => 35 ) 
    [3] => Array ( [a] => art4 [b] => 40 ) 
    [4] => Array ( [a] => art5 [b] => 50 ) 
    [5] => Array ( [a] => art6 [b] => 60 ) 
    )
*/


Answer the question

In order to leave comments, you need to log in

10 answer(s)
T
topbanana, 2013-04-15
@topbanana

Thank you all, I understand in which direction to think next!

F
frostosx, 2013-04-15
@frostosx

$notFound = array();
foreach ($new_data as $k => $v)
{
  if (isset($data[$k])) $data[$k] = $v;
    else $notFound[$k] = $v;

}


sometimes it's much easier =)

G
gaelpa, 2013-04-15
@gaelpa

You need an index.
Those. move the "key" field to the array index. If there is more than one key field, serialize.
those.

$dataToGo=[]; foreach($data as $entry) $dataToGo['$entry['key']]=$entry; 

If duplicates are possible, then be a little smarter.
How do you get this data? Maybe they can be obtained immediately in this form?

U
UZER2006, 2013-04-15
@UZER2006

Do you have the ability to turn both arrays into associative arrays with a key on the 'a' field?

Like like this
Array ( 
        [art1] => Array ( [b] => 15 ) 
        [art2] => Array ( [b] => 25 ) 
        [art3] => Array ( [b] => 35 ) 
        )

Then really everything can be solved in one cycle, doing a search in arrays by key.
If this is not possible, I don’t think there are methods faster than nested loops. Is it possible to somehow order both arrays so that finding a match for a specific entry from one array in another could be faster than brute force.
If there are no repetitions in both arrays (the 1st entry from the first array can correspond to no more than one entry from the second, and vice versa), as an optimization option, you can delete rows from the second array after each pass. There is a possibility of a slight acceleration due to the possible acceleration of each next iteration.

N
Nikita Gusakov, 2013-04-15
@hell0w0rd

$data = array_replace_recursive($data, $new_data);

Your test with print_p passes, did you miss something?

R
rsvasilyev, 2013-04-15
@rsvasilyev

php.net/manual/ru/function.array-merge.php
If the input arrays have the same string keys, then each subsequent value will replace the previous one. However, if the arrays have the same numeric keys, the value mentioned last will not replace the original value, but will be added to the end of the array.

S
Sergey, 2013-04-15
@serega_kaktus

$intersect = array_intersect_key($new_data, $data); //массив с одинаковыми ключами
array_merge($data, $intersect); //перезаписываем старые значения новыми
$diff = array_diff_key($new_data, $intersect); //получаем массив с элементами, которых нет в $data

Possible errors, as not tested. Yes, and I just googled the functions. But, I think, they will at least somehow simplify your cycles

L
LightSUN, 2013-04-15
@LightSUN

The fastest way is to sort both arrays by key, and then iterate over both arrays with two pointers (comparing the entries and moving the pointers manually). In addition, when I did this and tested it (for a long time, on version 5.1 somewhere), pointers worked 2 times faster than for / foreach.

E
egorinsk, 2013-04-15
@egorinsk

You need to make a an array index and use array_intersect_key/array_diff_key to find duplicate entries. Then there will be almost no cycles.

N
Nazar Mokrinsky, 2013-04-15
@nazarpc

Here's for PHP 5.5:

//исходные данные
$data[] = array("a" => 'art1', "b" => '10');
$data[] = array("a" => 'art2', "b" => '20');
$data[] = array("a" => 'art3', "b" => '30');
$data[] = array("a" => 'art4', "b" => '40');
$data[] = array("a" => 'art5', "b" => '50');
$data[] = array("a" => 'art6', "b" => '60');

//новые данные
$new_data[] = array("a" => 'art1', "b" => '15');
$new_data[] = array("a" => 'art2', "b" => '25');
$new_data[] = array("a" => 'art3', "b" => '35');

$new_data = array_column($new_data, 'b', 'a');

foreach($data as &$v)                   //для каждого элемента исходных данных
    if (isset($new_data[$v["a"]]));     //если найдены новые данные
        $v["b"] = $new_data[$v["a"]];   //меняем значение
        unset($new_data[$v["a"]]);      //экономим память
    }
unset($v);
print_r($data);

Implementation of array_column() for previous versions: https://github.com/nazar-pc/Useful-PHP-Functions/blob/master/upf.php#L1219

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question