tags:

views:

139

answers:

5

I have an array of my inventory (ITEMS A & B)

Items A & B are sold as sets of 1 x A & 2 x B.

The items also have various properties which don't affect how they are distributed into sets.

For example:

$inventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),  
array("B","BLACK")  
);

I want to redistribute the array $inventory to create $set(s) such that

$set[0] => Array
                (
                    [0] => array(A,PINK)
                    [1] => array(B,RED)
                    [2] => array(B,BLUE)

                )

$set[1] => Array
                (
                    [0] => array(A,MAUVE)
                    [1] => array(B,YELLOW)
                    [2] => array(B,GREEN)

                )

$set[2] => Array
                (
                    [0] => array(A,ORANGE)
                    [1] => array(B,BLACK)
                    [2] => NULL

                )

$set[3] => Array
                (
                    [0] => array(A,GREY)
                    [1] => NULL
                    [2] => NULL

                )

As you can see. The items are redistributed in the order in which they appear in the inventory to create a set of 1 x A & 2 x B. The colour doesn't matter when creating the set. But I need to be able to find out what colour went into which set after the $set array is created. Sets are created until all inventory is exhausted. Where an inventory item doesn't exist to go into a set, a NULL value is inserted.

Thanks in advance!

+1  A: 

I've assumed that all A's come before all B's:

$inventory=array(
                array("A","PINK"),
                array("A","MAUVE"),
                array("A","ORANGE"),
                array("A","GREY"),
                array("B","RED"),
                array("B","BLUE"),
                array("B","YELLOW"),
                array("B","GREEN"),  
                array("B","BLACK")  
                );

for($b_start_index = 0;$b_start_index<count($inventory);$b_start_index++) {
        if($inventory[$b_start_index][0] == 'B') {
                break;
        }
}

$set = array();
for($i=0,$j=$b_start_index;$i!=$b_start_index;$i++,$j+=2) {
        isset($inventory[$j])?$temp1=$inventory[$j]:$temp1 = null;
        isset($inventory[$j+1])?$temp2=$inventory[$j+1]:$temp2 = null;
        $set[] = array( $inventory[$i], $temp1, $temp2);                                                                                                                                                       
}
codaddict
Thanks for the quick reply!This certainly works. I'm still trying to decipher it (Arrays are still new to me), but from what I can see, I don't think its going to be very flexible for my purpose.The sets are not always going to be configured as (A,B,B). So I need to be able to do this "on the fly".Thank you for your effort
matt
+1  A: 

To make it easier to use your array, you should make it something like this

$inv['A'] = array(
    'PINK',
    'MAUVE',
    'ORANGE',
    'GREY'
);
$inv['B'] = array(
    'RED',
    'BLUE',
    'YELLOW',
    'GREEN',
    'BLACK'
);

This way you can loop through them separately.

$createdSets = $setsRecord = $bTemp = array();
$bMarker = 1;
$aIndex = $bIndex = 0;

foreach($inv['A'] as $singles){
    $bTemp[] = $singles;
    $setsRecord[$singles][] = $aIndex;
    for($i=$bIndex; $i < ($bMarker*2); ++$i) {
        //echo $bIndex.' - '.($bMarker*2).'<br/>';
        if(empty($inv['B'][$i])) {
            $bTemp[] = 'null';
        } else {
            $bTemp[] = $inv['B'][$i];
            $setsRecord[$inv['B'][$i]][] = $aIndex;
        }
    }

    $createdSets[] = $bTemp;
    $bTemp = array();
    ++$bMarker;
    ++$aIndex;
    $bIndex = $bIndex + 2;
}


echo '<pre>';
print_r($createdSets);
print_r($setsRecord);
echo '</pre>';

To turn your array into an associative array, something like this can be done

<?php
$inventory=array(
    array("A","PINK"),
    array("A","MAUVE"),
    array("A","ORANGE"),
    array("A","GREY"),
    array("B","RED"),
    array("B","BLUE"),
    array("B","YELLOW"),
    array("B","GREEN"),
    array("B","BLACK")
);

$inv = array();
foreach($inventory as $item){
    $inv[$item[0]][] = $item[1];
}
echo '<pre>';
print_r($inv);
echo '</pre>';
doingsitups
I hadn't thought about structuring the array as you have suggested. I can see how it would be easier to work with this. Your solution works,but relies too much on knowing the contents of the arrays before sorting.
matt
@matt - You should first define your storage in detail, otherwise you have to think too widely, too generically, better to work out your data store structure, otherwise known as Business Model, everything after that will be more efficient, because the starting point is well defined.
doingsitups
+1  A: 

Maybe you can use this function, assuming that:

... $inventory is already sorted (all A come before B)
... $inventory is a numeric array staring at index zero

// $set is the collection to which the generated sets are appended
// $inventory is your inventory, see the assumptions above
// $aCount - the number of A elements in a set
// $bCount - the number of B elements in a set
function makeSets(array &$sets, array $inventory, $aCount, $bCount) {
    // extract $aItems from $inventory and shorten $inventory by $aCount
    $aItems = array_splice($inventory, 0, $aCount);
    $bItems = array();

    // iterate over $inventory until a B item is found
    foreach($inventory as $index => $item) {
        if($item[0] == 'B') {
            // extract $bItems from $inventory and shorten $inventory by $bCount
            // break out of foreach loop after that
            $bItems = array_splice($inventory, $index, $bCount);
            break;
        }
    }

    // append $aItems and $bItems to $sets, padd this array with null if 
    // less then $aCount + $bCount added
    $sets[] = array_pad(array_merge($aItems, $bItems), $aCount + $bCount, null);

    // if there are still values left in $inventory, call 'makeSets' again
    if(count($inventory) > 0) makeSets($sets, $inventory, $aCount, $bCount);
}

$sets = array();
makeSets($sets, $inventory, 1, 2);
print_r($sets);

Since you mentioned that you dont have that much experience with arrays, here are the links to the php documentation for the functions I used in the above code:

  • array_splice — Remove a portion of the array and replace it with something else
  • array_merge — Merge one or more arrays
  • array_pad — Pad array to the specified length with a value
Max
+1  A: 

This code sorts inventory without any assumption on inventory ordering. You can specify pattern (in $aPattern), and order is obeyed. It also fills lacking entries with given default value.

<?php

# config
$aInventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),
array("B","BLACK"),
array("C","cRED"),
array("C","cBLUE"),
array("C","cYELLOW"),
array("C","cGREEN"),
array("C","cBLACK")
);

$aPattern = array('A','B','A','C');

$mDefault = null;

# preparation
$aCounter = array_count_values($aPattern);

$aCurrentCounter = $aCurrentIndex = array_fill_keys(array_unique($aPattern),0);

$aPositions = array();

$aFill = array();

foreach ($aPattern as $nPosition=>$sElement){

    $aPositions[$sElement] = array_keys($aPattern, $sElement);

    $aFill[$sElement]  = array_fill_keys($aPositions[$sElement], $mDefault);

} // foreach




$nTotalLine = count ($aPattern);

$aResult = array();

# main loop
foreach ($aInventory as $aItem){

    $sElement = $aItem[0];

    $nNeed = $aCounter[$sElement];

    $nHas  = $aCurrentCounter[$sElement];

    if ($nHas == $nNeed){

        $aCurrentIndex[$sElement]++;

        $aCurrentCounter[$sElement] = 1;

    } else {

        $aCurrentCounter[$sElement]++;

    } // if

    $nCurrentIndex = $aCurrentIndex[$sElement];

    if (!isset($aResult[$nCurrentIndex])){

        $aResult[$nCurrentIndex] = array();

    } // if

    $nCurrentPosition = $aPositions[$sElement][$aCurrentCounter[$sElement]-1];

    $aResult[$nCurrentIndex][$nCurrentPosition] = $aItem;

} // foreach

foreach ($aResult as &$aLine){

    if (count($aLine)<$nTotalLine){

        foreach ($aPositions as $sElement=>$aElementPositions){

            $nCurrentElements = count(array_keys($aLine,$sElement));

            if ($aCounter[$sElement] != $nCurrentElements){

                $aLine = $aLine + $aFill[$sElement];

            } // if

        } // foreach

    } // if

    ksort($aLine);

    # add empty items here

} // foreach

# output
var_dump($aResult);
ts
+1  A: 

Generic solution that requires you to specify a pattern of the form

$pattern = array('A','B','B');

The output will be in

$result = array();

The code :

// Convert to associative array
$inv = array();
foreach($inventory as $item)
    $inv[$item[0]][] = $item[1];

// Position counters : int -> int   
$count = array_fill(0, count($pattern),0);
$out = 0; // Number of counters that are "out" == "too far"
// Progression
while($out < count($count))
{
    $elem = array();
    // Select and increment corresponding counter
    foreach($pattern as $i => $pat)
    {
        $elem[] = $inv[ $pat ][ $count[$i]++ ];
        if($count[$i] == count($inv[$pat]))
          $out++;
    }
    $result[] = $elem;
}
Loïc Février