开发者

Help with PHP loop

Suppose I have a multi-dimensional array of the form:

array
(
    array('Set_ID' => 1, 'Item_ID' => 17, 'Item_Name' = 'Whatever'),
    array('Set_ID' => 1, 'Item_ID' => 18, 'Item_Name' = 'Blah'),
    array('Set_ID' => 2, 'Item_ID' => 19, 'Item_Name' = 'Yo')
)

The array has more sub-arrays, but that's the basic form-- Items in Sets.

How can I loop through this array so that I can echo the number of items in each set along with the all the items like so:

Set 1 has 2 Items: 17: Whatever and 18: Blah
Set 2 has 1 Items: 19: Yo

I'm aware that this could be done with two l开发者_如何学Gooops-- one to build an array, and another to loop through that array. However, I'd like to do this all with only one loop.

In your answer, you should assume that there are two display functions

display_set($id, $count) //echo's "Set $id has $count Items"
display_item($id, $name) //echo's "$id: $name"

UPDATE: Forgot to mention that the data is sorted by Set_ID because its from SQL


Right, all the examples below rely on an ordered set, the OP states it is ordered initially, but if needed a sort function could be:

// Sort set in to order
usort($displaySet,
      create_function('$a,$b',
                      'return ($a['Set_ID'] == $b['Set_ID']
                               ? ($a['Set_ID'] == $b['Item_ID']
                                  ? 0
                                  : ($a['Item_ID'] < $b['Item_ID']
                                     ? -1
                                     : 1))
                               : ($a['Set_ID'] < $b['Set_ID'] ? -1 : 1));'));

Straight example using a single loop:

// Initialise for the first set
$cSetID = $displaySet[0]['Set_ID'];
$cSetEntries = array();

foreach ($displaySet as $cItem) {
  if ($cSetID !== $cItem['Set_ID']) {
    // A new set has been seen, display old set
    display_set($cSetID, count($cSetEntries));
    echo ": " . implode(" and ", $cSetEntries) . "\n";
    $cSetID = $cItem['Set_ID'];
    $cSetEntries = array();
  }

  // Store item display for later
  ob_start();
  display_item($cItem['Item_ID'], $cItem['Item_Name');
  $cSetEntries[] = ob_get_clean();
}

// Perform last set display
display_set($cSetID, count($cSetEntries));
echo ": " . implode(" and ", $cSetEntries) . "\n";

Using a recursive function it could be something like this:

// Define recursive display function
function displayItemList($itemList) {
  if (!empty($itemList)) {
    $cItem = array_shift($itemList);
    display_item($cItem['Item_ID'], $cItem['Item_Name');

    if (!empty($itemList)) {
      echo " and ";
    }
  }

  displayItemList($itemList);
}

// Initialise for the first set
$cSetID = $displaySet[0]['Set_ID'];
$cSetEntries = array();

foreach ($displaySet as $cItem) {
  if ($cSetID !== $cItem['Set_ID']) {
    // A new set has been seen, display old set
    display_set($cSetID, count($cSetEntries));
    echo ": ";
    displayItemList($cSetEntries);
    echo "\n";
    $cSetID = $cItem['Set_ID'];
    $cSetEntries = array();
  }

  // Store item for later
  $cSetEntries[] = $cItem;
}

// Perform last set display
display_set($cSetID, count($cSetEntries));
echo ": ";
displayItemList($cSetEntries);
echo "\n";

Amusingly, it can be one single recursive function:

function displaySetList($setList, $itemList = NULL) {
  // First call, start process
  if ($itemList === NULL) {
    $itemList = array(array_shift($setList));
    displaySetList($setList, $itemList);

    return;
  }

  // Check for display item list mode
  if ($setList === false) {
    // Output first entry in the list
    $cItem = array_shift($itemList);
    display_item($cItem['Item_ID'], $cItem['Item_Name']);

    if (!empty($itemList)) {
      // Output the next
      echo " and ";
      displaySetList(false, $itemList);
    } else {
      echo "\n";
    }

    return;
  }

  if (empty($setList) || $setList[0]['Set_ID'] != $itemList[0]['Set_ID']) {
    // New Set detected, output set
    display_set($itemList[0]['Set_ID'], count($itemList));
    echo ": ";
    displaySetList(false, $itemList);
    $itemList = array();
  }

  // Add next item and carry on
  $itemList[] = array_shift($setList);
  displaySetList($setList, $itemList);
}

// Execute the function
displaySetList($displaySet);

Note that the recursive example here is grossly inefficient, a double loop is by far the quickest.


<?php
$sets = array();
foreach ($items as $item)
{
    if (!array_key_exists($item['Set_ID'], $sets))
    {
        $sets[$item['Set_ID']] = array();
    }
    $sets[$item['Set_ID']][] = $item;
}
foreach ($sets as $setID => $items)
{
    echo 'Set ' . $setID . ' has ' . count($items) . ' Items: ';
    foreach ($items as $item)
    {
         echo $item['Item_ID'] . ' ' . $item['Item_Name'];
    }
}
?>

Something like this i guess?

EDIT: After i posted this i saw the display functions where added. But you get the point.


The need to not print out any items until we know how many there are in the set makes this difficult. At some point, we'll need to doing some buffering, or else backtracking. However, if I'm allowed internal loops, and sets are contiguous in the "master" array, then with some hacking around:

$set = 0;
$items;

foreach ($arr as $a) {
    if ($a['Set_ID'] != $set) {
        if ($set != 0) {
            display_set($set, count($items));
            foreach ($items as $i)
                display_item($i)
        }
        $set = $a['Set_ID'];
        $items = array();
    }
    $items[] = $a;
}


How about this:

$previous_set = false;

$items = '';
$item_count = 0;

foreach ($rows as $row)
{
 if ($row['Set_ID'] != $previous_set)
 {
  if ($previous_set)
  {
   echo display_set($row['Set_ID'], $item_count);
   echo $items;
  }
  $previous_class = $row['Set_ID'];
  $item_count = 0;
  $items = '';
 }
 $items .= display_item($row['Item_ID'], $row['Title']);
 $item_count++;

}
echo display_set($row['Set_ID'], $item_count);
echo $items;
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜