5

I'm designing a package engine for my catalog. Here you can add a certain ammount of products to the package and a discount. When you order products the script have to detect which package deals apply to your order.

Here is my code:

// packages
$packages["package1"] = array(1,1,2);
$packages["package2"] = array(1,2);

//orderlist
$orderlist = array(1,1,2,1,2,2);

// put the order list in a temp array
$temp_product_array = $orderlist;

foreach($packages as $pname => $package_array)
{
  $no_more_package = 0;
  do
  {
    // put the package products in a temp array
    $temp_package_array = $package_array;

    $is_packages_array = array_intersect($temp_package_array,$temp_product_array);

    // if all package values are present
    if(count($is_packages_array) == count($temp_package_array))
    {
      // add package name
      $packages_in_order[] =  $pname;

      // filter the package out of the product list but keep duplicate values
      foreach($temp_product_array as $key1 => $pid1)
      {
        foreach($temp_package_array as $key2 => $pid2)
        {
          if($pid1==$pid2)
          {
            unset($temp_product_array[$key1]);
            unset($temp_package_array[$key2]);
            break;  // after removing go to the next product to prevent double erasing
          }
        }
      }
    }
    else
    {
      $no_more_package = 1;
    }

  }
  while($no_more_package<1);
}

print_r($packages_in_order);
print_r($temp_product_array);

The result is:

Array ( [0] => package1 [1] => package1 ) Array ( [5] => 2 ) 

But I want the result to be:

Array ( [0] => package1 [1] => package2 ) Array ( [5] => 2 )

I tried array_diff, array_intersect but they all do not work well with duplicate values.

Does anyone has a better/working way of solving this?
(PS because of different sources I cannot work with associative arrays)

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
  • So must orderlist contain the same products as the package (and in the same order) for it to match? – cmbuckley Oct 17 '12 at 09:52
  • The orderlist contains any number of pruduct ids in any order possible. (In the script the packages are ordered by discount.) – Tim Schutte Oct 17 '12 at 11:16

2 Answers2

0

I would devide the problem. Part of it is to locate the package inside the list. An existing function that does exactly that has been named consecutive_values in a probably related question: Searching for consecutive values in an array.

With it is it possible to locate an array within another array in the exact order. This is probably what you want.

The part left is to search for the packages then and is pretty straight forward. If you understood your question right, you want to return left-overs as well:

list($found, $rest) = find_packages($packages, $orderlist);
var_dump($found, $rest);

function find_packages(array $packages, array $list)
{
    $found = array();
    foreach($packages as $name => $package) {
        # consecutive_values() is @link https://stackoverflow.com/a/6300893/367456
        $has = consecutive_values($package, $list);
        if ($has === -1) continue;
        $found[] = $name;
        array_splice($list, $has, count($package));
    }

    return array($found, $list);
}

Output:

array(2) {
  [0] =>
  string(8) "package1"
  [1] =>
  string(8) "package2"
}
array(1) {
  [0] =>
  int(2)
}

Edit: Searching for the same package multiple times needs a slight modification. Here the an inner while loop is created that needs a break if the current package is not found:

function find_packages(array $packages, array $list)
{
    $found = array();
    foreach($packages as $name => $package) {
        while (true) {
            # consecutive_values() is @link https://stackoverflow.com/a/6300893/367456
            $has = consecutive_values($package, $list);
            if ($has === -1) break;
            $found[] = $name;
            array_splice($list, $has, count($package));
        }
    }

    return array($found, $list);
}
Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • This works perfectly. I going tot test it with more packages and product ideas. Thank you!! – Tim Schutte Oct 17 '12 at 12:03
  • I also had a breaktrough at about the same moment. I will post my solution. Curious on best way to address this problem.. – Tim Schutte Oct 17 '12 at 12:04
  • I would always go with dividing the problem into functions. The code is easier to follow and to change. Also you can test the leaf functions that they properly work quite easily. – hakre Oct 17 '12 at 12:10
  • There is still a problem with this solution. If I change the package order the result should be: Array ( [0] => package1 [1] => package1 [2] => package1 ) Array ( [1] => 0 [2] => 0 ) But this solution gives: array(2) { [0]=> string(8) "package1" [1]=> string(8) "package2" } array(1) { [0]=> int(2) } – Tim Schutte Oct 17 '12 at 12:16
  • Ah okay, you want to look multiple times for the same package, that was not clear to me. It's a slight change, I added it and explained a little. – hakre Oct 17 '12 at 12:27
  • Yeah, sorry maybe I didn't mentioned that. – Tim Schutte Oct 17 '12 at 12:36
  • Getting closer. It seems the outcome of this is: Array ( [0] => package1 [1] => package1 ) Array ( [0] => 1 [1] => 2 ) , It looks like it skips 1 package. – Tim Schutte Oct 17 '12 at 12:59
  • No it does not skip it. There is no more room in the list that has been already consumed by the existing packages to match another package. – hakre Oct 17 '12 at 13:01
  • Hi hakre, how can I send you a test script which test both ways? I dont't want to pollute the comments to much ;) – Tim Schutte Oct 17 '12 at 13:35
  • @TimSchutte: First upvote ;) Then use gist on github or pastebin. leave a link here, if I've got the time I'll look into it. – hakre Oct 17 '12 at 13:38
  • I do not have enough reputation :). I hope to get some more soon. This is my first active use of this forum. LINK: https://gist.github.com/3905592 – Tim Schutte Oct 17 '12 at 13:47
0
// packages
$packages["package1"] = array(1,1,2);
$packages["package2"] = array(1,2);

//orderlist
$orderlist = array(1,1,1,2,2,2);



// put the order list in a temp array
$temp_product_array = $orderlist;
$product_count_array = array_count_values($temp_product_array);

foreach($packages as $pname => $temp_package_array)
{
  $no_more_package = 0;
  do
  {
    $test_package_array = array();

    foreach($temp_package_array as $key => $pid)
    {
      // check if the product is still in the order totals 
      if(isset($product_count_array[$pid]) && $product_count_array[$pid]>0)
       {
         $product_count_array[$pid]--;
         $test_package_array[] = $pid;
       }
       else
       {
         $no_more_package = 1;
       }
    }
    // check if the found products match the package count
    if(count($temp_package_array)==count($test_package_array))
    {
      $packages_in_order[] = $pname;
    }
    else
    {
      // add the extracted products in case of incomplete package 
      foreach($test_package_array as $pid)
       {
         $product_count_array[$pid]++;
       }
    }


  }
  while($no_more_package<1);
}

print_r($packages_in_order);
print_r($product_count_array);