ассоциативный — PHP: как обеспечить, массив содержит все комбинации значений

Хорошо, вот проблема: у меня есть массив со следующей структурой:

$array[0] 'fruit' -> 'apple', 'origin' -> 'usa', 'price' -> '$1'
$array[1] 'fruit' -> 'apple', 'origin' -> 'canada', 'price' -> '$1'
$array[2] 'fruit' -> 'pear', 'origin' -> 'usa', 'price' -> '$1'
$array[3] 'fruit' -> 'peach', 'origin' -> 'spain', 'price' -> '$2'
$array[4] 'fruit' -> 'peach', 'origin' -> 'greece', 'price' -> '$0.5'

Этот массив содержит много фруктов, с разным происхождением. Цель состоит в том, чтобы убедиться, что для каждого фрукта все возможно (= all in the array contained origins) происхождение присутствует. Если одна из комбинаций отсутствует, в массив следует добавить новую запись с 'price' -> 'tba',

Итак, в приведенном выше примере, есть три разных фруктов (apple, pear, peach) и четыре разных происхождения (usa, canada, spain, greece),

Мне нужно проверить, есть ли для каждого фрукта все возможные источники, и если нет, добавьте их с ценой tba.

Как я могу решить это in a performant way? Я уже пытался использовать дополнительный массив, который содержит только происхождение и фрукты, но пока не повезло.

Изменить: Вот ожидаемый результат (сортировка не так важна в данный момент):

$array[0] 'fruit' -> 'apple', 'origin' -> 'usa', 'price' -> '$1'
$array[1] 'fruit' -> 'apple', 'origin' -> 'canada', 'price' -> '$1'
$array[2] 'fruit' -> 'apple', 'origin' -> 'spain', 'price' -> 'tba'
$array[3] 'fruit' -> 'apple', 'origin' -> 'greece', 'price' -> 'tba'
$array[4] 'fruit' -> 'pear', 'origin' -> 'usa', 'price' -> '$1'
$array[5] 'fruit' -> 'pear', 'origin' -> 'canada', 'price' -> 'tba'
$array[6] 'fruit' -> 'pear', 'origin' -> 'spain', 'price' -> 'tba'
$array[7] 'fruit' -> 'pear', 'origin' -> 'greece', 'price' -> 'tba'
$array[8] 'fruit' -> 'peach', 'origin' -> 'usa', 'price' -> 'tba'
$array[9] 'fruit' -> 'peach', 'origin' -> 'canada', 'price' -> 'tba'
$array[10] 'fruit' -> 'peach', 'origin' -> 'spain', 'price' -> '$2'
$array[11] 'fruit' -> 'peach', 'origin' -> 'greece', 'price' -> '$0.5'

То, что я пробовал до сих пор, но которое не сработало из-за а) ошибок с переменными и б) тайм-аут 30 с, который был достигнут:

$fruits = array('apple', 'pear', 'peach');
$origins = array('usa', 'canada', 'spain', 'greece');

Затем я попытался перебрать основной массив и проверить каждую комбинацию:

foreach ($fruits as $f)
{
foreach ($origins as $o)
{
if (!current(array_filter($array, function($item) { return $f == $item['fruit'] && $o == $item['origin']; }));)
{
// add the fruit-origin combination the the main array
}
}
}

Что не работает из-за того, что $ f и $ o недоступны внутри функции.

3

Решение

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

Для этого рекомендуется использовать другой формат массива. Вы можете сгенерировать это так:

<?php
$produceList = [
['fruit' => 'apple', 'origin' => 'usa',    'price' => '$1'],
['fruit' => 'apple', 'origin' => 'canada', 'price' => '$1'],
['fruit' => 'pear',  'origin' => 'usa',    'price' => '$1'],
['fruit' => 'peach', 'origin' => 'spain',  'price' => '$2'],
['fruit' => 'peach', 'origin' => 'greece', 'price' => '$0.5'],
];

$newProduceList = array();
foreach ($produceList as $product) {
$newProduceList[$product['fruit']][$product['origin']] = $product['price'];
}

Это даст вам массив, где вы можете использовать фрукты и происхождение в качестве индексов:

Array(
[apple] => Array(
[usa] => $1
[canada] => $1
)
[pear] => Array(
[usa] => $1
)
[peach] => Array(
[spain] => $2
[greece] => $0.5
)
)

Таким образом, легко получить конкретную цену на продукт:

<?php
if (isset($newProduceList['apple']['germany'])) {
echo $newProduceList['apple']['germany'];
} else {
echo 'tba';
}
// Output: tba

Если вы добавите функцию в качестве обертки вокруг этого фрагмента кода, вы можете использовать «getProducePrice (‘apple’, ‘canada’);» чтобы вернуть либо цену, либо «tba».

<?php
function getProducePrice($fruit, $origin) {
global $newProduceList; // it's bad style to use globals, used just as example
if (isset($newProduceList[$fruit][$origin])) {
return $newProduceList[$fruit][$origin];
} else {
return 'tba';
}
}

echo getProducePrice('pear', 'usa');
// Output: $1

Используя оригинальный массив

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

<?php
$price = 'tba';
foreach ($produceList as $produce) {
if ($produce['fruit']!='peach' || $produce['origin']!='spain') continue;
// found the specific fruit from the specific origin
$price = $produce['price'];
break; // stop iterating through the rest of the array.
}
echo $price;
// Output: $2
0

Другие решения

Похоже, я нашел решение:

if (!current(array_filter($array, function($item) use($f, $o) { return $f == $item['fruit'] && $o == $item['origin']; }));)
{
// add the fruit-origin combination the the main array
}

Я скучал по use($f, $o) часть. Я не уверен, что это может быть изменено с точки зрения производительности, но на данный момент это достаточно хорошо, я думаю. Тем не менее, улучшения всегда приветствуются 🙂

0