views:

162

answers:

4

Suppose I have some serially numbered items that are 1-n units wide, that need to be displayed in rows. Each row is m units wide. I need some pseudo-code that will output the rows, for me, so that the m-width limit is kept. This is not a knapsack problem, as the items must remain in serial number order - empty spaces at the end of rows are fine.

I've been chasing my tail over this, partly because I need it in both PHP and jQuery/javascript, hence the request for pseudo-code....

+3  A: 
while (!items.isEmpty()) {
  rowRemain = m;
  rowContents = [];
  while (!items.isEmpty() && rowRemain > items[0].width) {
    i = items.shift();
    rowRemain -= i.width
    rowContents.push(i);
  }
  rows.push(rowContents);
}

Running time is Θ(number of items)

bdonlan
Ah ha - a much cleaner solution than my own reply! That's the sort of thing I was looking for... thanks...
Dycey
A: 

Modulus is your friend. I would do something like:

$items = array(/* Your list of stuff */);
$count = 0;
$maxUnitsPerRow = 4; // Your "m" above

while ($item = $items[$count]) {
 if ($count % $maxUnitsPerRow == 0) {
    $row = new row();
 }
$row->addItemToRow($item);
$count++;
}
Bryan
When items can have width other than 1, this won't work.
bdonlan
Maybe I'm being dense, but I don't get why it wouldn't work for items with a width other than 1.If items is the list of all things that have to go in all rows, and you create a new row each time the modulus of the count versus the row width is zero, you'll always have rows of the right count.Where it won't work is if there are serial numbers that are skipped in the sequence, unless you add empty placeholders for those serial numbers or use some other method of iterating through the list.
Bryan
I can't see where you've taken account of the fact that each item can have a different width...
Dycey
A: 

For what it's worth, I think I have what I was looking for, for PHP - but not sure if there is a simpler way...

<?php
// working with just a simple array of widths...
$items     = array(1,1,1,2,1,1,2,1);
$row_width = 0;
$max_width = 2;

echo "Begin\n"; // begin first row
foreach($items as $item=>$item_width) {
  // can we add item_width to row without going over?
  $row_width += $item_width;
  if($row_width < $max_width) {
    echo "$item_width ";
  } else if($row_width == $max_width) {
    echo "$item_width";
    echo "\nEnd\nBegin\n"; // end last row, begin new row
    $row_width = 0;
  } else if($row_width == 2* $max_width) {
    echo "\nEnd\nBegin\n"; // end last row, begin new row
    echo "$item_width";
    echo "\nEnd\n"; // end new row
    $row_width = 0;
    if($item < count($items)) echo "Begin\n"; // new row
  } else if($row_width > $max_width) {
    echo "\nEnd\nBegin\n"; // end last row, begin new row
    echo "$item_width";
    $row_width = $item_width;
  }
}
echo "\nEnd\n"; // end last row

?>
Dycey
A: 

Here is an alternative php code ...

function arrayMaxWidthString($items, $maxWidth) {
    $out = array();
    if (empty($items)) {
     return $out;
    }

    $row = $maxWidth;
    $i = 0;

    $item = array_shift($items);
    $row -= strlen($item);
    $out[0] = $item;

    foreach ($items as $item) {
     $l = strlen($item);
     $tmp = ($l + 1);
     if ($row >= $tmp) {
      $row -= $tmp;
      $out[$i] = (($row !== $maxWidth) ? $out[$i] . ' ' : '') . $item;
     } elseif ($row === $maxWidth) {
      $out[$i] = $item;
      ++$i;
     } else {
      ++$i;
      $row = $maxWidth - $l;
      $out[$i] = $item;
     }
    }
    return $out;
}
OIS
I like it, fewer tests...
Dycey