3

Ok, here's the problem: I have an array with the following structure:

$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'

This array contains a lot of fruits, with a lot of different origins. The goal is, to make sure that for every fruit, all possible (= all in the array contained origins) origins are present. If one of the combinations is missing, a new record should be added to the array with 'price' -> 'tba'.

So, in the above example, there are three different fruits (apple, pear, peach) and four different origins (usa, canada, spain, greece).

I need to check if for each fruit all possible origins are available, and if not, add them with the tba price.

How can I solve this in a performant way? I already tried using additional array which only contain the origins, and the fruits, but no luck so far.

Edit: Here is the expected result (sorting is not that important at the moment):

$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'

What I tried so far, but which didn't work because of a) errors with the variables, and b) a timeout of 30s which has been reached:

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

Then I tried to iterate over the main array and check every combination:

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
      }
   }
}

Which does not work due to the fact that $f and $o is not available inside the function.

amkx
  • 41
  • 3

2 Answers2

0

I'm not sure how you are using this list, but you probably want to get the price for a specific fruit from a specific origin without having to go through the whole array each time.

For that it is a good idea to use a different array format. You can generate it like this:

<?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'];
}

This will give you an array where you can use the fruit and origin as indices:

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

This way it's easy to get the specific price for a product:

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

If you add a function as wrapper around this piece of code, you can then use "getProducePrice('apple', 'canada');" to have either the price or "tba" returned.

<?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

Using the original array

If you are stuck with the original array format you always have to walk through the array to search for a specific fruit like this:

<?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
Balu
  • 1
  • 2
0

Looks like I found the solution:

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
      }

I was missing the use($f, $o) part. I'm not sure if this can be tweaked performance wise, but for the moment it's good enough I guess. However, improvements are always welcome :)

amkx
  • 41
  • 3