skip to Main Content

Basically, I have an array that contains other arrays as its value. Each one of these "sub" arrays are mapped.

For example:

$items = [
    ["type" => 1, "text" => "Type of 1"],
    ["type" => 1, "text" => "Type of 1"],
    ["type" => 1, "text" => "Type of 1"],
    ["type" => 1, "text" => "Type of 1"],
    ["type" => 2, "text" => "Type of 2"],
    ["type" => 2, "text" => "Type of 2"],
    ["type" => 2, "text" => "Type of 2"],
    ["type" => 2, "text" => "Type of 2"],
];

Now, there could be any amount of each type, in varying orders. For example, the first item in $items could be of type 1, the next four of type 2, the next 8 of type 1 again, so on and so forth. Keep in mind, my actual code has 8 different types as possible values, and the above is just an example.

Basically, I’m trying to define a set limit–let’s say, 3. In ascending order, and also ordering by type as ascending (so starting with items of type 1, then 2, then 3, etc then restarting), I want to basically "echo" out the "type" of the first 3 items with type 1, then move on to do 3 items of type 3, so on and so forth until I’ve done 3 of type 8. Then, I want to move back up the array, and do it again, but only with the items I’ve not used yet, and do the same thing.

I want to do this until I’ve used every item in the array.

I have scoured the web and even here on Stackoverflow, and I can’t seem to find an algorithm to match what I’m trying to do. I can’t seem to find much, and I’m beginning to wonder if the algorithm I’m looking for doesn’t actually exist yet but I’m probably wrong or just searching in the wrong places.

My current thought process was to use a separate array called $groups, use a foreach loop eight different times, and in each one, continue the loop if it’s not for the specific type. For example:

// My above $items array example here

// Initialize $groups
$groups = [];

// foreach for type 1
foreach ($items as $item) {
    // If any of the items are not of type 1, ignore and continue
    if ($item["type"] !== 1) {
        continue;
    }
    // Otherwise, push to $groups
    array_push($groups, $item["type"];
}

// foreach for type 2
foreach ($items as $item) {
    // If any of the items are not of type 2, ignore and continue
    if ($item["type"] !== 2) {
        continue;
    }
    // Otherwise, push to $groups
    array_push($groups, $item["type"];
}

// Do this for each possible type, up to type 8

This would at least make the output of $groups with implode("", $groups) be something along the lines of: 1111111122222222333333334444444555555556666677777777888 but not only does this seem extremely inefficient and backwards, I just feel there is a better way to go about it or another angle I’m missing. I’m absolutely stumped.

To further explain, I’d like the output to be something more like this:
111222333444555666777888111222333444555666777888

2

Answers


  1. First of all

    I can’t seem to find an algorithm to match what I’m trying to do. I can’t seem to find much, and I’m beginning to wonder if the algorithm I’m looking for doesn’t actually exist yet

    yes, you are right, it does not exists and should not exists ever, the Algorithm should not be exists to output a specific result. it should be used to perform a specific behavior/function rather than achieve/output a specific result.

    however, you can sort an array based on it’s key using uasort like following:

    $items = [
        ["type" => 1, "text" => "Type of 1"],
        ["type" => 1, "text" => "Type of 1"],
        ["type" => 1, "text" => "Type of 1"],
        ["type" => 1, "text" => "Type of 1"],
        ["type" => 2, "text" => "Type of 2"],
        ["type" => 2, "text" => "Type of 2"],
        ["type" => 2, "text" => "Type of 2"],
        ["type" => 2, "text" => "Type of 2"],
    ];
    
    uasort($items, fn ($a, $b) => $a > $b ? 1 : -1); 
    
    print_r($items);
    

    Then, you can use something like array_column in a combination with implode to extract the item as a value in a one-linear fashion, or you can use the foreach loops as you already mentioned.

    Login or Signup to reply.
  2. This first creates a count of each type, then loops till there is nothing output. The inner loop will output the number of items you want (as long as that type has something left to output) adjusting the remaining left as you go along.

    I’ve commented the code which should explain it clearer.

    // Create text lookup array, indexed by type
    $types = array_column($items, null, 'type');
    // Count the number of each type there are
    $breakdown = array_count_values(array_column($items, 'type'));
    
    // Sort by the key to create desired order
    ksort($breakdown);
    
    $breakSize = 3;
    
    do {
        // If flag doesn't change then nothing is left to output
        $anyOutput = false;
        foreach ($breakdown as $key => &$countLeft) {
            // Loop while less than the break size but also with something left
            for ($i = 0; $i < $breakSize && $countLeft > 0; $i++) {
                echo $types[$key]['text'] . PHP_EOL;
                // Adjust amount left
                $countLeft--;
                // Flag something put out
                $anyOutput = true;
            }
        }
    } while ($anyOutput);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search