Home > Back-end >  how to push array elements on three arrays based on specific percentage and specific sequence in php
how to push array elements on three arrays based on specific percentage and specific sequence in php

Time:11-23

i'm trying to solve this problem:

I've 3 percentage (%) stored in three callers: (Caller1, Caller2 and Caller3) like this:

$callers = [
    'Caller1' => 50,
    'Caller2' => 30,
    'Caller3' => 20
];

and I've ten numbers to call, i want to assign the numbers to each caller based on their percentage.

Example:

if numbers are (1, 2, 3 ,4 ,5 ,6 ,7 ,8,9,10), then the calls should be split like the bellow sequence based on their percentage (Note: like the below sequence):

  • Caller1: 1,4,7,9,10
  • Caller2: 2,5,8
  • Caller3: 3,6

my code like this:

class MyClassName
{
    // Each caller with their call percentage
    private $callers = [
        'caller1' => 50,
        'caller2' => 30,
        'caller3' => 20
    ];

    public $caller1 = array();
    public $caller2 = array();
    public $caller3 = array();

    public function splitCalls(array $numbers)
    {
        //count numbers
        //get spesific number of each percentage
        //loop on numbers and push numbers into 3 arrays based on percentage
        $count = count($numbers);
        $callerOneNumbers = floor(($this->callers['caller1'] / 100) * $count);
        $callerTwoNumbers = floor(($this->callers['caller2'] / 100) * $count);
        $callerThreeNumbers = floor(($this->callers['caller3'] / 100) * $count);  

        for ($i = 0; $i <= $count; $i  ) {
            if (count($this->caller1) < $callerOneNumbers) {
                array_push($this->caller1, $numbers[$i]);
                $i  ;
            }

            if (count($this->caller2) < $callerTwoNumbers) {
                array_push($this->caller2, $numbers[$i]);
                $i  ;
            }

            if (count($this->caller3) < $callerThreeNumbers) {
                array_push($this->caller3, $numbers[$i]);
                $i  ;
            }
        }
    }
}

$obj = new MyClassName;
$split = $obj->splitCalls([1, 2, 3, 4, 5, 6, 7, 8,9,10]);
var_dump($obj->caller1);
var_dump($obj->caller2);
var_dump($obj->caller3);

my result like this:

array (size=3)
  0 => int 1
  1 => int 5
  2 => int 9
C:\wamp64\www\test.php:102:
array (size=3)
  0 => int 2
  1 => int 6
  2 => int 10
C:\wamp64\www\test.php:103:
array (size=2)
  0 => int 3
  1 => int 7

but i want like this:

array (size=3)
  0 => int 1
  1 => int 4
  2 => int 7
  3 => int 9
  4 => int 10


array (size=2)
  0 => int 2
  1 => int 5
  2 => int 8


array (size=1)
  0 => int 3
  1 => int 6

any help please

CodePudding user response:

The main problem with your code is that at ever loop, you have $i (in the for() loop). This skips 1 array entry per loop (4 and 8 in this example). So a simple solution to your problem would be to change the loop to...

for ($i = 0; $i < $count;) {

CodePudding user response:

You can be flexible with any amount by calculating a distribution array. Then you know how many items need to go in there and can sequential iterate through it.

$distribute = function(array $callers, array $data): array {
    $distribution = array_values(array_map(fn($percentage) => (int) $percentage/100 * count($data), $callers));
    $results = [];
    $index = 0;
    $length = count($callers);
    foreach ($data as $value) {
        $added = false;
        while (!$added) {
            $row = $index % $length;
            if ($distribution[$row] > 0) {
                $results[$row][] = $value;
                $distribution[$row]--;
                $added = true;
            }
            $index  ;
        }
    }

    return array_combine(array_keys($callers), $results);
};

echo json_encode($distribute(['Caller1' => 50, 'Caller2' => 30, 'Caller3' => 20], range(1,10)));

Output

JSON encoded for nicer display.

{"Caller1":[1,4,7,9,10],"Caller2":[2,5,8],"Caller3":[3,6]}

CodePudding user response:

Your code has too many hard codes in my opinion. So I think you should create it to be more dynamic. So we can handle a if the data changes.

Here is my suggestion:

<?php

class MyClassName
{
    // Each caller with their call percentage
    private $callers = [
        'caller1' => 50,
        'caller2' => 30,
        'caller3' => 20
    ];

    public $calls = array();

    public function splitCalls(array $numbers)
    {
        //count numbers
        //get spesific number of each percentage
        //loop on numbers and push numbers into 3 arrays based on percentage
        $numberCount = count($numbers);
        $callerCount = count($this->callers);
        $j=1;
        foreach ($this->callers as $caller => $percent) {
            $callerNumber = floor(($percent / 100) * $numberCount);
            $this->calls[$caller] = [];
            for ($i = $j; $i <= $numberCount; $i =$callerCount) {
                $this->calls[$caller][] = $i;
                if(count($this->calls[$caller]) >= $callerNumber){
                    break;
                }
            }
            $j  ;
        }
    }
}

$obj = new MyClassName;
$split = $obj->splitCalls([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var_dump($obj->calls);

output will be:

array(3) {
  'caller1' =>
  array(4) {
    [0] =>
    int(1)
    [1] =>
    int(4)
    [2] =>
    int(7)
    [3] =>
    int(10)
  }
  'caller2' =>
  array(3) {
    [0] =>
    int(2)
    [1] =>
    int(5)
    [2] =>
    int(8)
  }
  'caller3' =>
  array(2) {
    [0] =>
    int(3)
    [1] =>
    int(6)
  }
}

the result is same as you expected.

  • Related