I need the variable $tsp to be evenly added to the value in the "count" field, but only in those fields where the "count" is greater than 0 .
$tsp = 9;
$fin = [
"1701" => ["total_space" => 0, "count" => 0],
"1702" => ["total_space" => 0, "count" => 0],
"1703" => ["total_space" => 20, "count" => 20],
"1704" => ["total_space" => 28, "count" => 28]
];
that 's what the result should be
$tsp = 9;
$fin = [
"1701" => ["total_space" => 0, "count" => 0],
"1702" => ["total_space" => 0, "count" => 0],
"1703" => ["total_space" => 20, "count" => 25], // +5
"1704" => ["total_space" => 28, "count" => 32] // +4
];
I wrote a loop, but it increases both fields by 9,
for ($i = $tsp; $i > 0; $i--) {
foreach ($fin as $dt => $s) {
if ($s['count'] > 0) {
$fin[$dt]['count'] = $s['count'] + 1;
$tsp = $tsp - 1;
}
}
}
The issue with your attempted implementation is that you iterate over all entries in the $fin array and increment the counter for each $tsp. You only want to increment one of those, not all ...
This would be a possible solution:
<?php
$tsp=9;
$fin = [
"1701"=> ["total_space"=> 0, "count"=> 0],
"1702"=> ["total_space"=> 0, "count"=> 0],
"1703"=> ["total_space"=> 20, "count"=> 20],
"1704"=> ["total_space"=> 28, "count"=> 28]
];
while ($tsp > 0) {
array_walk($fin, function(&$s) use (&$tsp) {
if ($tsp > 0 && $s['count'] > 0) {
$s['count']++;
$tsp--;
}
});
}
print_r($fin);
The output obviously is:
Array
(
[1701] => Array
(
[total_space] => 0
[count] => 0
)
[1702] => Array
(
[total_space] => 0
[count] => 0
)
[1703] => Array
(
[total_space] => 20
[count] => 25
)
[1704] => Array
(
[total_space] => 28
[count] => 32
)
)
Rather than brute forcing the arithmetic with single increments and decrements in a loop which re-checks for 0 counts, use a technique with lower time complexity that doesn't ever revisit 0 count rows.
My snippet filters the array once, then only adds value to qualifying rows once.
Code: (Demo)
$qualifiers = array_filter($fin, fn($row) => $row['count']);
$divideBy = count($qualifiers);
foreach ($qualifiers as $id => $row) {
$tsp -= $toAdd = ceil($tsp / $divideBy);
--$divideBy;
$fin[$id]['count'] += $toAdd;
}
var_export($fin);
Related
I need to sort my multidimensional array using two rules.
My array looks like this:
$array = [
['label' => 12, 'countValue' => 5],
['label' => 4, 'countValue' => 78],
['label' => 9, 'countValue' => 5],
['label' => 64, 'countValue' => 0],
['label' => 3, 'countValue' => 60],
['label' => 19, 'countValue' => 0],
['label' => 7, 'countValue' => 5],
];
I need rows with ['countValue'] = 0 to be move to the back and
Sort the rows based on their label value in an ascending direction.
Desired result:
$array = [
['label' => 3, 'countValue' => 60],
['label' => 4, 'countValue' => 78],
['label' => 7, 'countValue' => 5],
['label' => 9, 'countValue' => 5],
['label' => 12, 'countValue' => 5],
['label' => 19, 'countValue' => 0],
['label' => 64, 'countValue' => 0],
];
I have the following code:
public function sortOptionsByName($a, $b)
{
$x = trim($a['label']);
$y = trim($b['label']);
if ($x == '') return 1;
if ($y == '') return -1;
if (is_numeric($x) && is_numeric($y)){
if ($x == $y)
return 0;
return ($x > $y ? 1 : -1);
}
else {
return strcasecmp($x, $y);
}
}
public function sortOptionsByCounts($a, $b)
{
if ($a['countValue'] == $b['countValue']) {
return 0;
}
return ($a['countValue'] < $b['countValue'] ? 1 : -1);
}
Something like...
public function sortOptionsByCountsAndByName($a, $b)
{
if ($a['countValue'] == 0 && $b['countValue'] == 0) {
return -2
}
else {
$this->sortOptionsByName($a, $b)
}
}
First compare values with zero. PHP casts boolean to integer, so you can just subtract to get -1, 0 , 1. And then compare another value when thw 1st comparing returns 0
public function sortOptionsByCountsAndByName($a, $b)
{
$res = ($a['countValue'] == 0) - ($b['countValue'] == 0);
return ($res ? $res : $this->sortOptionsByName($a, $b));
}
You need to sort your data twice, once by name and once by count value. I would suggest to use usort() and implement two custom compare methods:
<?php
function cmpName($a, $b) {
return strnatcmp($a['Name'], $b['Name']);
}
function cmpCountValue($a, $b) {
if ($a['CountValue'] == 0)
return 1;
if ($b['CountValue'] == 0)
return -1;
return cmpName($a, $b);
}
$a[0]['Name'] = 'Bob';
$a[0]['CountValue'] = 0;
$a[1]['Name'] = 'Christal';
$a[1]['CountValue'] = 42;
$a[2]['Name'] = 'Alice';
$a[2]['CountValue'] = 23;
$a[3]['Name'] = 'Jack';
$a[3]['CountValue'] = 1;
$a[4]['Name'] = 'Alex';
$a[4]['CountValue'] = 58;
usort($a, "cmpName");
usort($a, "cmpCountValue");
foreach ($a as $item) {
echo $item['Name'] . ": " . $item['CountValue'] . "<br />";
}
?>
The output is:
Alex: 58
Alice: 23
Christal: 42
Jack: 1
Bob: 0
Online demo: See here
Just bake your sorting rules into two arrays with symmetric elements and separate them with a spaceship operator.
When sorting ASC, false comes before true, so by checking if the countValue is "falsey", true evaluations will be pushed to the back of the pack.
The order of the elements in the sorting callback says:
Prioritize non-zero countValue rows.
When there is a tie on the first rule, break the tie by sorting on label ASC.
Code: (Demo)
usort(
$array,
fn($a, $b) =>
[!$a['countValue'], $a['label']]
<=>
[!$b['countValue'], $b['label']]
);
var_export($array);
Output:
array (
0 =>
array (
'label' => 3,
'countValue' => 60,
),
1 =>
array (
'label' => 4,
'countValue' => 78,
),
2 =>
array (
'label' => 7,
'countValue' => 5,
),
3 =>
array (
'label' => 9,
'countValue' => 5,
),
4 =>
array (
'label' => 12,
'countValue' => 5,
),
5 =>
array (
'label' => 19,
'countValue' => 0,
),
6 =>
array (
'label' => 64,
'countValue' => 0,
),
)
I have an array that contains periods from 1 - 13. Sometimes the array doesn't contain data for all periods and I need to fill in the missing ones, for example:
$array = [
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
];
For this case I need to run a php loop to fill in the missing first 6 periods with 0 y values. I've tried a variety of loops but with no joy.
Desired output:
[
['period' => 1, 'y' => 0],
['period' => 2, 'y' => 0],
['period' => 3, 'y' => 0],
['period' => 4, 'y' => 0],
['period' => 5, 'y' => 0],
['period' => 6, 'y' => 0],
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
]
You can get good semantics with using the standard array methods. For example:
<?php
$in = [
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
];
// collect available periods
$available = array_column($in, 'period');
// calculate missing periods
$missing = array_diff(range(1, 13), $available);
// transform missing to correct format
$addition = array_map(function ($period) { return ['period' => $period, 'y' => 0]; }, $missing);
// add missing to input
$out = array_merge($in, $addition);
// sort by period
usort($out, function ($a, $b) {
return $a['period'] <=> $b['period'];
});
// done
print_r($out);
demo: https://3v4l.org/2fDYW
start by filling the whole array you need with all period with a value of 0.
Then for each period you get in your data array, use the period id to update the value of period in the right place in the array.
Hope it helps...
You can try this
for ($i=1 $<=13, $i++) {
$exits = false;
foreach ($array as $key => $value) {
if ($value['period'] == $key) {
$exits = true;
}
}
if ($exits) {
$array[] = ['period' => $i , 'y' => 0];
}
}
This should solve your problem:
Let's say $p_array is your array that contains periods from 1 - 13.
// get all periods number that is not missed
$not_missed_periods = array();
foreach ($p_array as $p_each)
{
$not_missed_periods[] = $p_each['period'];
}
// loop for checking all valid periods i.e 1-13
for ($i=1; $i<=13; $i++)
{
// check if $i OR period is not in $not_missed_periods
if (!in_array($i, $not_missed_periods)) {
$p_array[] = array('period' => $i, 'y' => 0);
}
}
print_r($p_array);
Assuming your $array is sorted by period.
You can create a new array that copies the content or your $array and set a new content for missing periods.
$new_array = [];
for ($i = 1, $j = 0; $i <= 13; $i++) {
if ($array[$j]['period'] == $i) {
$new_array[] = $array[$j]; //copy contents
$j++;
} else {
$new_array[] = [ 'period' => $i, 'y' => 0 ]; // set new contents
}
}
Use array_column() to generate a lookup array from your input array -- effectively applying associative, numeric, first-level keys without disturbing the original row data.
Then iterate from 1 to 13. If the current iteration's integer is found in the lookup, then push the found row; otherwise push the default row containing the incremented value.
Code: (Demo)
$lookup = array_column($data, null, 'period');
$result = [];
for ($i = 1; $i <= 13; ++$i) {
$result[] = $lookup[$i] ?? ['period' => $i, 'y' => 0];
}
var_export($result);
I have the next array
[
['id' => 30, 'count' => 3],
['id' => 45, 'count' => 7]
]
I need it to be
[
30 => ['count' => 3],
45 => ['count' => 7]
]
What I did
$formatted = [];
foreach ($services as $service) {
$formatted[$service['id']] = [
'count' => $service['count']
];
}
What I'd like is a more elegant solution without the temporary $formatted variable. Thanks!
Update. Thanks a lot #rtrigoso !
With the laravel collection, my code looks next
$services->reduce(function ($carry, $item) {
$carry[$item['id']] = ['count' => $item['count']];
return $carry;
});
You can do this in one line with array_column:
$array = array_column($array, null, 'id');
The one difference between your desired output is that this will still contain the id key in the second level of the array, like so:
[
30 => ['id' => 30, 'count' => 3],
45 => ['id' => 45, 'count' => 7],
]
but that hopefully shouldn't cause any problems. If you do need to remove it, you can do it with something like:
$array = array_map(function ($e) {
unset($e['id']);
return $e;
}, $array);
This approach is probably best if your rows could potentially have a lot more keys in them in future, i.e. it's quicker to list the keys to remove rather than the ones to keep. If not, and you'll only have a count, then to be honest your original example is probably the best you'll get.
You can use array_reduce
$x_arr = array(
array('id' => 30, 'count' => 3),
array('id' => 45, 'count' => 7),
);
$y_arr = array_reduce($x_arr, function ($result, $item) {
$result[$item['id']] = array('count' => $item['count']);
return $result;
}, array());
print_r($y_arr);
It will give you your desired result:
Array
(
[30] => Array
(
[count] => 3
)
[45] => Array
(
[count] => 7
)
)
I have an array of X and y coordinates, how can I find the nearest point to the 0,0 and sort them from the nearest to the farthest point from the 0,0?
<?php
$array = array(array("x" => 10,
"y" => 10),
array("x" => 120,
"y" => 560),
array("x" => 950,
"y" => 23),
array("x" => 78,
"y" => 40),);
?>
Thanks in advance and sorry for my english :|
Using usort:
<?php
//your array
$array = array(array("x" => 10,
"y" => 10),
array("x" => 120,
"y" => 560),
array("x" => 950,
"y" => 23),
array("x" => 78,
"y" => 40),);
//define a compare function
function cmp($a,$b){
//get the squared distance of a and b
$distA_SQ = $a['x']*$a['x']+$a['y']*$a['y'];
$distB_SQ = $b['x']*$b['x']+$b['y']*$b['y'];
//if squared distances are the same, return 0
if($distA_SQ==$distB_SQ)return 0;
//distances are not the same so return 1 if a larger than b or -1 if b larger than a
return $distA_SQ>$distB_SQ?1:-1;
}
//run the sort function
usort($array, 'cmp');
//output the array
var_dump($array);
http://codepad.org/OBH1cskb
And to determine if distance of point A is greater than B, you don't need to sqrt the distance. It is expensive and unnecessary.
Edit: Added comments to code and explanation below
This uses usort, which uses a user defined compare function. usort will look over the array performing a quicksort by calling your compare function and passing in two values at a time (usually passed in as $a and $b) and expects your compare function to return -1 if $a is less than $b, 0 if $a is equal to $b or 1 if $a is greater than $b. you can read more on usort in the manual.
Create a new array and put x, y and their distance from (0,0) in it.
$distArray = array();
foreach($distArray as $point):
$distArray[] = array($point['x'],$point['y'],dist($point['x'],$point['y']));
endforeach;
now sort this array by it's third element. I believe writing the dist() function would be easy.
EDIT: I suggest to keep the x and y in your array so when you sort the result array, you know which item is which point.
Check out http://www.ltcconline.net/greenl/courses/154/factor/circle.htm
function calculateDistance($x,$y)
{
//apply formula
return sqrt(pow($x, 2) + pow($y, 2)); //<--Sammitch pointed out directly
}
$data = array();
foreach($array as $key=>$distance)
{//this is better because you can have points that have the same distance
$data[calculateDistance($distance['x'],$distance['y'])][] = $key;
}
ksort($data);
RESULT
in:
$array = array(array("x" => 10,
"y" => 10),
array("x" => 120,
"y" => 560),
array("x" => 120,
"y" => 560),
array("x" => 950,
"y" => 23),
array("x" => 78,
"y" => 40));
output:
array (size=4)
14 => //<--key is the distance and the value are the keys from your array
array (size=1)
0 => int 0
87 =>
array (size=1)
0 => int 4
572 =>
array (size=2)
0 => int 1
1 => int 2
950 =>
array (size=1)
0 => int 3
try this
<?php
$array = array(array("x" => 10,
"y" => 10),
array("x" => 120,
"y" => 560),
array("x" => 950,
"y" => 23),
array("x" => 78,
"y" => 40),);
$distance = array();
$req_array = array();
foreach($array as $subArray)
{
$distance[] = sqrt(pow(($subArray[x],2)+pow(($subArray[y],2));
}
asort($distance);
foreach($distance as $key=>$value)
{
$req_array[] = $array[$key];
}
print_r($distance);
print_r($req_array);
?>
$array = array(
array("x" => 10, "y" => 10),
array("x" => 120, "y" => 560),
array("x" => 950, "y" => 23),
array("x" => 78, "y" => 40)
);
$start_point_array = $array;
$end_point_array = $array;
$farthest_points = array();
$farthest_distance = 0;
foreach($start_point_array as $index => $start_point) {
for($i = $index + 1; $i < count($end_point_array); $i++) {
$end_point = $end_point_array[$i];
$distance = sqrt(pow($end_point['x'] - $start_point['x'], 2) + pow($end_point['y'] - $start_point['y'], 2));
if($distance > $farthest_distance) {
$farthest_distance = $distance;
$farthest_points = array($start_point, $end_point); // Or whatever
}
}
}
I want to combine two arrays like this:
1st array:
array( "ATTENDED" => 1,
"TENTATIVE" => 2, //
"REJECTED" => 3,
"OUTSTANDING" => 4,
"ACCEPTED" => 6
);
2nd Array:
array ( 1 => 29,
4 => 30,
6 => 47
);
I want to get the results like this:
array ( 'ATTENDED' => 29,
'OUTSTANDING' => 30,
'ACCEPTED' => 47
);
2nd array is flexible. I can flip keys and values.
or better yet:
array( "ATTENDED" => 29,
"TENTATIVE" => 0, //
"REJECTED" => 0,
"OUTSTANDING" => 30,
"ACCEPTED" => 47
);
I know there must be a simple solution.
Any ideas?
foreach ($arr1 as $k1 => $v1) {
$arr1[$k1] = isset($arr2[$v1]) ? $arr2[$v1] : 0;
}
edit-
This is without an explicit loop, although I don't think this is really better, but maybe cooler though.
$mapped = array_map(function($valFromArr1) use ($arr2) {
return isset($arr2[$valFromArr1]) ? $arr2[$valFromArr1] : 0;
}, $arr1);
I can't think of a sane way to just use pure php functions.
$labels = array(
"ATTENDED" => 1,
"TENTATIVE" => 2,
"REJECTED" => 3,
"OUTSTANDING" => 4,
"ACCEPTED" => 6
);
$values = array(
1 => 29,
4 => 30,
6 => 47
);
$results = array();
foreach ($labels as $label => $id) {
$results[$label] = array_key_exists($id, $values) ? $values[$id] : 0;
}