Как избежать вложенного цикла и вообще по-человечески сделать?

Добрый день!
Ломаю голову, листаю справочник функций, ничего не могу придумать лучше…

Есть 2 массива: исходный — $data, и массив новых данных — $new_data.
Цель — в случае соответствия значений двух ключей, заменить старые данные новыми.
Еще нужно знать каким элементам новых данных не были найдены соответсвия в исходных.
В реальном массиве исходных данных ключей больше.

Простой способ — вложенные циклы. Но, оба массива могут иметь по 1000 элементов… Моих слабых знаний не хватает, чтобы сделать по-человечески. Подскажите, как это оптимизировать?

//исходные данные
$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 ) 
		)
*/


  • Вопрос задан
  • 7863 просмотра
Решения вопроса 1
@topbanana Автор вопроса
Всем спасибо, я понял в каком направлении думать дальше!
Ответ написан
Пригласить эксперта
Ответы на вопрос 7
frostosx
@frostosx
$notFound = array();
foreach ($new_data as $k => $v)
{
	if (isset($data[$k])) $data[$k] = $v;
		else $notFound[$k] = $v;

}



иногда всё в разы проще =)
Ответ написан
gaelpa
@gaelpa
Вам нужен индекс.
Т.е. вынести «ключевое» поле в индекс массива. Если ключевое поле не одно — сериализовать.
т.е.
$dataToGo=[]; foreach($data as $entry) $dataToGo['$entry['key']]=$entry; 

Если возможны дубликаты, то чуток помудрить.

А каким образом вы получаете эти данные? Может их сразу можно получить в таком виде?
Ответ написан
Комментировать
hell0w0rd
@hell0w0rd
Просто разработчик
$data = array_replace_recursive($data, $new_data);

Ваш тест с print_p проходит, что-то упустил?
Ответ написан
@serega_kaktus
Программист-самоучка, фрилансер
$intersect = array_intersect_key($new_data, $data); //массив с одинаковыми ключами
array_merge($data, $intersect); //перезаписываем старые значения новыми
$diff = array_diff_key($new_data, $intersect); //получаем массив с элементами, которых нет в $data


Возможны ошибки, так как не тестил. Да и функции только что нагуглил. Но, думаю, позволят хоть как то упростить ваши циклы
Ответ написан
LightSUN
@LightSUN
Самое быстрое это отсортировать оба массива по ключу, а затем пройтись двумя указателями по обоим массивам (сравнивая записи и двигая указатели вручную). Кроме того, когда делал подобное и тестировал (давно, на версии 5.1 где-то), указатели работали раза в 2 быстрее чем for/foreach.
Ответ написан
Комментировать
@egorinsk
Надо сделать a индексом массива и использовать array_intersect_key/array_diff_key для поиска одинаковых записей. Тогда будет почти без циклов.
Ответ написан
Комментировать
nazarpc
@nazarpc
Open Source enthusiast
Вот вам для 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);

Реализация array_column() для предыдущих версий: https://github.com/nazar-pc/Useful-PHP-Functions/blob/master/upf.php#L1219
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы