开发者

How do I redistribute an array into another array of a certain "shape". PHP

开发者 https://www.devze.com 2023-01-14 14:42 出处:网络
I have an array of my inventory (ITEMS A & B) Items A & B are sold as sets of 1 x A & 2 x B.

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 in开发者_如何转开发to 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!


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


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


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


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);


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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号