Как объединить два многомерных массива на основе пар ключ-значение без вложенного цикла foreach? - PullRequest
0 голосов
/ 26 октября 2018

У меня есть 2 массива, $fileArr и $noteArr.

$fileArr - это список файлов. Файл может быть связан с более чем одной историей. Итак, в этом $fileArr вы видите все файлы в истории, которые мне нужно перечислить. Обратите внимание, что CAR.jpg ([processId]=>111) связан с [storyId]=>1 и [storyId]=>2. Таким образом, CAR.jpg будет указан дважды.

Я хочу взять все заметки из $noteArr и поместить их в $fileArr, сопоставив [processId]. Таким образом, каждый экземпляр CAR.jpg будет иметь 2 примечания, а TRUCK.jpg - нет.

Мой текущий $ fileArr

Array
(
[0] => Array
    (
        [fileName] => CAR.jpg
        [processId] => 111
        [storyId] => 1
    )

[1] => Array
    (
        [fileName] => CAR.jpg
        [processId] => 111
        [storyId] => 2
    )

[2] => Array
    (
        [fileName] => TRUCK.jpg
        [processId] => 222
        [storyId] => 3
    )
)

Мой текущий $ noteArr

Array
(
[0] => Array
    (
        [noteId] => 50
        [note] => this is a note
        [processId] => 111
    )

[1] => Array
    (
        [noteId] => 51
        [note] => and this is also a note
        [processId] => 111
    )

)

Мой предполагаемый новый массив с примечаниями, помещенными под файл, совпадая с идентификатором процесса Array ( [0] => Array ( [fileName] => CAR.jpg [processId] => 111 [storyId] => 1 [notes] => Array ( [50] => Array ( [noteId] => 50 [note] => this is a note [processId] => 111 ) [51] => Array ( [noteId] => 51 [note] => and this is also a note [processId] => 111 ) ) ) [1] => Array ( [fileName] => CAR.jpg [processId] => 111 [storyId] => 2 [notes] => Array ( [50] => Array ( [noteId] => 50 [note] => this is a note [processId] => 111 ) [51] => Array ( [noteId] => 51 [note] => and this is also a note [processId] => 111 ) ) ) [2] => Array ( [fileName] => TRUCK.jpg [processId] => 222 [storyId] => 3 ) ) Я могу сделать это с помощью кода, который я написал ниже, но не хочу использовать цикл внутри цикла. Есть ли другой способ, которым я могу достичь этого?

Мой текущий код (цикл внутри цикла)

$newArr = array();

$i = 0;
foreach($fileArr as $file){
    $newArr[$i] = $file;

    if(count($noteArr)>0){
        foreach($noteArr as $note){
            if($file['processId']==$note['processId']){
                $newArr[$i]['notes'][$note['id']] = $note;
            }
        }
    }
    $i++;
}

Ответы [ 4 ]

0 голосов
/ 26 октября 2018

Вы можете использовать кучу функций массива в PHP.

array_walk($fileArr, function(&$item) use ($noteArr) {
  $processId = $item['processId'];
  $foundNotes = array_filter($noteArr, function($note) use ($processId) {
    return $note['processId'] === $processId;
  });

  if (!empty($foundNotes)) {
    $item['notes'] = $foundNotes;
  }
});

Это изменит ваш $fileArr, чтобы стать тем, кем, я полагаю, вы хотите.

FWIW Я не думаю, что вложенный цикл - это плохой способ сделать это.хотя этот код решает проблему так, как вы просите, я думаю, что это сложнее понять.

0 голосов
/ 26 октября 2018

Если вы хотите удалить вложенный цикл, чтобы сократить время выполнения, вы можете сделать это за два прохода

// save items of noteArr for each processId
$temp = [];
foreach($noteArr as $k => $note) {
   $temp[$note['processId']][] = $note;
}

// and add saved sub-arrays to the source one
$newArr = [];
$i = 0;
foreach($fileArr as $file){
    $newArr[$i] = $file;
    if(isset($temp[$file['processId']])) 
         $newArr[$i]['notes'] = $temp[$file['processId'];
    $i++;
}
0 голосов
/ 26 октября 2018

Привет, я делаю быстрый взгляд, как бы я решил вашу проблему.К сожалению, он оказался во вложенном цикле, но мне не нужно зацикливать какой-либо элемент (в вашем решении вы повторяете все заметки для каждого файла) дважды.

function sortProcessId($a, $b)
{
    return $a['processId'] <=> $b['processId'];
}

$newArr = [];

usort($fileArr, 'sortProcessId');
usort($noteArr, 'sortProcessId');
rsort($noteArr);
$currentProcessId = 0;
$currentNote = array_pop($noteArr);
$newArr = [];
foreach ($fileArr as $key => $file){
    $newArr[$key] = $file;
    if ($currentProcessId === $file['processId']) {
        $newArr[$key]['notes'] = [ $newArr[--$key]['notes']];
        continue;
    }
    $newArr[$key]['notes'] = [];
    $currentProcessId = $file['processId'];
    while ($file['processId'] == $currentNote['processId']) {
        $newArr[$key]['notes'][$currentNote['noteId']] = $currentNote;
        $currentNote = array_pop($noteArr);
    }
}
var_dump($newArr);

Дополнительные примечания: я использовал rsort дляиспользовать array_pop, который должен быть быстрее, чем array_shift

0 голосов
/ 26 октября 2018

Без вложенных циклов вы можете создать функцию и передать по ссылке:

function addNote(array &$array, array $notes)
{
    $array['notes'] = [];
    foreach ($notes as $note)
    {
        if ($note['processId'] == $array['processId']) {
            $array['notes'][] = $note;
        }
    }
    return $array;
}

foreach ($fileArr as &$file) {
    addNote($file, $notes);
}

var_dump($fileArr);

Посмотрите, как работает здесь https://3v4l.org/u37UL

...