Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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)

share|improve this question
    
So must orderlist contain the same products as the package (and in the same order) for it to match? –  cbuckley Oct 17 '12 at 9:52
    
Is the order important? –  hakre Oct 17 '12 at 9:55
    
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 Answers 2

// 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);
share|improve this answer

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 http://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 http://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);
}
share|improve this answer
    
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

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.