I have two arrays, both will always be the same count length. One has doubles mixed with integers, the second has textual (string only) values. They do correlate so I need them both to stay in order. Sorry no keys to work with (by design).
I need to sum the values where I have duplicates in the array that has strings.
Example
$dataLabelGraph = array(3, 8, 1, 4.85, 1, 0.5, 6.01, 7);
$dataCalcGraph = array("Coding", "Web development - Coding", "Meeting", "Coding", "Coding", "Content", "Coding", "Coding");
So my algorithm should look like this after
$dataLabelGraph = array(21.86, 8, 1, 0.5);
$dataCalcGraph = array("Coding", "Web development - Coding", "Meeting", "Content");
I was trying to adapt this solution, from the awesome brain of Martin D. # https://stackoverflow.com/a/22071693/12835769
$records_array = array("Coding", "Web development - Coding", "Meeting", "Coding", "Coding", "Content", "Coding");
$quantities_array = array(3, 8, 1, 4.85, 1, 0.5, 6.01, 7);
$new_array = array();
foreach ($records_array as $record_position => $new_array_key){
$new_array[$new_array_key] += $quantities_array[$record_position];
}
var_dump($new_array);
Gives something like this, which is close but I need them to remain in two separate arrays
array (size=4)
'Coding' => float 21.86
'Web development - Coding' => int 8
'Meeting' => int 1
'Content' => float 0.5
Any help to get me over the line would be immensely helpful. Kudos.
Group by the "name" and sum as you iterate. When the loop is finished, split the keys and the values into separate arrays.
Code: (Demo)
$records = [
"Coding",
"Web development - Coding",
"Meeting",
"Coding",
"Coding",
"Content",
"Coding",
"Coding"
];
$quantities = [
3,
8,
1,
4.85,
1,
0.5,
6.01,
7
];
$result = [];
foreach ($records as $index => $label){
$result[$label] = ($result[$label] ?? 0) + $quantities[$index];
}
var_export(array_keys($result));
var_export(array_values($result));
Outputs:
array (
0 => 'Coding',
1 => 'Web development - Coding',
2 => 'Meeting',
3 => 'Content',
)
array (
0 => 21.86,
1 => 8,
2 => 1,
3 => 0.5,
)
Related
This question already has answers here:
PHP - Check if two arrays are equal
(19 answers)
Check if two arrays have the same values (regardless of value order) [duplicate]
(13 answers)
How to check if PHP associative arrays are equal, ignoring key ordering?
(1 answer)
Closed 4 years ago.
I'm trying to match 2 arrays that look like below.
$system = array('blue' => 1, 'red' => 2, 'green' => 3, 'purple' => 4);
$public = array('blue' => 1, 'red' => 2, 'green' => 3, 'purple' => 4);
My problem is, I need the array keys of both arrays to be the same value and same count.
Which means:
// passes - both arrays have the same key values and same counts of each key
$system = array('blue' => 1, 'red' => 2, 'green' => 3, 'purple' => 4);
$public = array('blue' => 1, 'red' => 2, 'green' => 3, 'purple' => 4);
// fails - $public does not have 'blue' => 1
$system = array('blue' => 1, 'red' => 2, 'green' => 3, 'purple' => 4);
$public = array('red' => 2, 'green' => 3, 'purple' => 4);
// should fail - $public has 2 'blue' => 1
$system = array('blue' => 1, 'red' => 2, 'green' => 3, 'purple' => 4);
$public = array('blue' => 1, 'blue' => 1, 'red' => 2, 'green' => 3, 'purple' => 4);
I've tried using array_diff_keys, array_diff and other php functions, but none can catch extra keys with the same value (i.e. if 'blue' => 1, is repeated it still passes)
What's a good way to solve this?
When you write two values with same key in PHP, the second one will overwrite the value from the first (and this is not an error). Below is what I did on the PHP interactive CLI (run it with php -a):
php > $x = ["x" => 1, "x" => 2, "y" => 2];
php > var_dump($x);
array(2) {
["x"]=>
int(2)
["y"]=>
int(2)
}
So array_diff seems to be working correctly. You are just expecting PHP to behave in a different way than it actually does!
Right now I have an array of objects in PHP, and some of them have a duplicate value for "id". I'm trying to remove the duplicates but I want to keep the one with the lowest value for "qty".
I know how you'd normally remove duplicate whole objects from an array, but not only if one value is a duplicate and how to keep the lower of another value.
Example:
[
{
id: 1,
qty: 200
},
{
id: 1,
qty: 190
},
{
id: 2,
qty: 10
},
{
id: 2,
qty: 12
},
{
id: 2,
qty: 10
},
{
id: 3,
qty: 5
},
{
id: 4,
qty: 5
},
{
id: 4,
qty: 2
},
]
What I want to end up with would be..
[
{
id: 4,
qty: 2
},
{
id: 3,
qty: 5
},
{
id: 2,
qty: 10
},
{
id: 1,
qty: 190
}
]
Is this possible?
That looks almost like JSON, so assuming you $array = json_decode($json, true) to an associative array:
array_multisort(array_column($array, 'qty'), SORT_DESC, $array);
$result = array_column($array, null, 'id');
Extract an array of qty and sort that descending, sorting the original by that
Extract from that an array with id as the key
Extracting with array_column() will cause the last id key to overwrite the previous ones. The last one will be the one with the lowest qty since it was sorted DESCending.
If you need to get it back to a JSON object, then just re-index:
$json = json_encode(array_values($result));
AbraCadaver came up with such a good answer, but I worked hard to come up with mine, so I want to share it in case it is useful for someone. If anything, it may provide useful for an expanded or more complex array. I went the route of creating a nested loop. Here is the code:
$newArray = [];
for ($i = 0; $i < count($myArray); $i++)
{
$id_column = array_column($newArray, 'id');
$qty_column = array_column($newArray, 'qty');
if (!in_array($myArray[$i]['id'],$id_column)) {
array_push($newArray, $myArray[$i]);
}
else {
$id_pos = array_search($myArray[$i]['id'],$id_column);
if ($myArray[$i]['qty'] < $qty_column[$id_pos])
{
array_splice($newArray,$id_pos,1,$myArray[$i]);
}
}
}
Basically I create a new empty array. I loop through each element of the original array to see if it's in the new array. If not, I add it, and if it is already in the new array, then I check the new Array to see if the qty for that id is higher, if so, I splice in the current row.
It is not necessary to involve a sorting algorithm -- that would only negatively impact the script performance.
If a row's id is not yet presented in the output array, push it into the output array with a temporary first level key. If the row's id is already in the output array, but has a lesser qty, then overwrite the row in the output array.
This task never needs more than one loop. If you want to remove the temporary first level keys, then call array_values() after looping.
Code: (Demo)
$data = [
['id' => 1, 'qty' => 200],
['id' => 1, 'qty' => 190],
['id' => 2, 'qty' => 10],
['id' => 2, 'qty' => 12],
['id' => 2, 'qty' => 10],
['id' => 3, 'qty' => 5],
['id' => 4, 'qty' => 5],
['id' => 4, 'qty' => 2],
];
$result = [];
foreach ($data as $row) {
if (!isset($result[$row['id']]) || $row['qty'] < $result[$row['id']]['qty']) {
$result[$row['id']] = $row;
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'id' => 1,
'qty' => 190,
),
1 =>
array (
'id' => 2,
'qty' => 10,
),
2 =>
array (
'id' => 3,
'qty' => 5,
),
3 =>
array (
'id' => 4,
'qty' => 2,
),
)
Here is one of the possible answers, assuming $your_input contains your data as a string…
Method 1
<?php
$your_input=preg_replace('#\s*\{\s*id\s*:\s*([^,]+?),\s*qty\s*:\s*([^,\}]+?)\s*\}\s*(,)?#','{"id":"$1","qty":"$2"}$3',$your_input);
$your_input=json_decode($your_input,true);
$result=[];
foreach($your_input as $a)
if (!array_key_exists($a['id'],$result) or $a['qty']<$result[$a['id']]['qty'])
{
$result[$a['id']]['id']=$a['id'];
$result[$a['id']]['qty']=$a['qty'];
}
$result=json_encode(array_values($result),JSON_PRETTY_PRINT);
echo '<pre>';
print_r($result);
Method 2
<?php
$your_input=str_replace([' ',':'],['"','":'],$your_input);
$your_input=json_decode($your_input,true);
usort($your_input,function($a,$b){return $a['qty']==$b['qty']?0:($a['qty']>$b['qty']?-1:1);});
foreach($your_input as $b) $result[$b['id']]=['id'=>$b['id'],'qty'=>$b['qty']];
$result=json_encode(array_values($result),JSON_PRETTY_PRINT);
echo '<pre>';
print_r($result);
After Method 1 or Method 2 the $result variable should contains the following data string…
[
{
"id": 1,
"qty": 190
},
{
"id": 2,
"qty": 10
}
]
For instance, I have an array with N elements, and I need that it have M elements where M is multiple of O. Example:
[signature: array array_fill_multiple(array, int o, mixed filler = null)]
$array = [ 1, 2 ];
array_fill_multiple($array, 1); // => [ 1, 2 ]
array_fill_multiple($array, 2); // => [ 1, 2 ]
array_fill_multiple($array, 3); // => [ 1, 2, null ]
array_fill_multiple($array, 4); // => [ 1, 2, null, null ]
$array = [ 1, 2, 3 ];
array_fill_multiple($array, 1); // => [ 1, 2, 3 ]
array_fill_multiple($array, 2); // => [ 1, 2, 3, null ]
array_fill_multiple($array, 3); // => [ 1, 2, 3 ]
array_fill_multiple($array, 4); // => [ 1, 2, 3, null ]
$array = [ 1, 2, 3, 4 ];
array_fill_multiple($array, 5, 0); // => [ 1, 2, 3, 4, 0 ]
I can do it with a for, but I guess that using native methods are possible, are not?
Edit
To explain better what I want, it need result in an array with multiples of O elements. So if I like that this array contains multiples of 5, it need result in an array with 0, 5, 10, 15, 20, 25... elements (zero included, if empty).
If my array have 15 elements, and I expect multiple of 2, it'll fit to next multiple of 2 after or equal 15, or be, 16. So only 1 padding will be created.
If my array have 3 elements and I expect multiple of 5, it'll fit to next multiple of 5, after or equal to 3, or be, the own 5. So 2 paddings will be created.
If my array have 10 elements and I expect multiple of 10, it'll fit to next multiple of 10, after or igual to 10, or be, the own 10. So none padding will be created, because my array yet is multiple of 10.
If my array is empty, it'll return an empty array.
I guess that array_pad() will help. Just need calculate the second argument to know what is the next multiple of O based on array count.
Something like this
$a = [1, 2, 3];
$multiple = 5;
$size = sizeof($a);
// first way
var_dump(array_pad($a, ceil($size / $multiple) * $multiple, null));
// second way
var_dump(array_pad($a, $size + ($size % $multiple ? $multiple - $size % $multiple : 0), null));
Second one is more accurate than first one. Let's suppose that you have array with 10000000000000001 items (on 64 system). Now you have to pad with multiplier 11.
$size = 10000000000000001 * 11;
$multiple = 11;
var_dump($size);
// int(110000000000000011)
// first way
ini_set('precision', 18);
var_dump(ceil($size / $multiple) * $multiple);
// double(110000000000000000)
// second way
var_dump($size + ($size % $multiple ? $multiple - $size % $multiple : 0));
// int(110000000000000011)
Now you see that first way produces wrong value because float values has less precision than int.
Basically you just replace "array_fill_multiple" with "array_pad" and it'll work :)
$array = [ 1, 2 ];
array_pad($array, 1, null); // => [ 1, 2 ]
array_pad($array, 2, null); // => [ 1, 2 ]
array_pad($array, 3, null); // => [ 1, 2, null ]
array_pad($array, 4, null); // => [ 1, 2, null, null ]
$array = [ 1, 2, 3 ];
array_pad($array, 1, null); // => [ 1, 2, 3 ]
array_pad($array, 2, null); // => [ 1, 2, 3, null ]
array_pad($array, 3, null); // => [ 1, 2, 3 ]
array_pad($array, 4, null); // => [ 1, 2, 3, null ]
$array = [ 1, 2, 3, 4 ];
array_pad($array, 5, 0); // => [ 1, 2, 3, 4, 0 ]
Im trying to make a multidimensional array with two columns. Name and Counter. I can do a single array with all the names. But I dont know how to make it multidimensional and be able to still update the counters. Code i got so far is
if (!in_array($prodname, $da)){
array_push($da, $prodname);
}
and then I can dump it back out with a foreach. How do I make it two dimensional? How can I say alright this exists update the old value? etc.
If you only need name and counter then you should just be able to use a normal array:
$nameCountArray = array();
foreach($names as $name){
if(!array_key_exists($name,$nameCountArray)){
$nameCountArray[$name] = 1;
}else{
$nameCountArray[$name] = $nameCountArray[$name] + 1;
}
}
If you do need multidimensional arrays these are just arrays of arrays and can be accessed as such. A good example of this is using a 2d array to store locations (say on a 3 by 3 grid):
$twoDArray = array(
0 => array(0 => 1,
1 => 4,
2 => 7),
1 => array(0 => 2,
1 => 5,
2 => 8),
2 => array(0 => 3,
1 => 6,
2 => 9)
);
//Grab the item at 1,2
$item = $twoDArray[1][2];//Will give '8'
Supposing you want $da to look like this:
Array(
"name1" => array("score1" => 80, "score2" => 100),
"name2" => array("score1" => 50, "score2" => 60),
"name3" => array("score1" => 90, "score2" => 80),
...
)
Then all you need to do is something like:
function setScore($prodName, $scoreName, $score)
{
global $da;
if (!array_key_exists($prodName, $da)) {
$da[$prodName] = array();
}
$da[$prodName][$scoreName] = $score;
}
setScore("name1", "score1", 80);
setScore("name1", "score2", 100);
setScore("name2", "score1", 50);
...
Unless I'm misunderstanding your question, which is very possible.
I ran into an issue with a data feed I need to import where for some reason the feed producer has decided to provide data that should clearly be either INT or FLOAT as strings-- like this:
$CASES_SOLD = "THREE";
$CASES_STOCKED = "FOUR";
Is there a way in PHP to interpret the text string as the actual integer?
EDIT: I should be more clear-- I need to have the $cases_sold etc. as an integer-- so I can then manipulate them as digits, store in database as INT, etc.
Use an associative array, for example:
$map = array("ONE" => 1, "TWO" => 2, "THREE" => 3, "FOUR" => 4);
$CASES_SOLD = $map["THREE"]; // 3
If you are only interested by "converting" one to nine, you may use the following code:
$convert = array('one' => 1,
'two' => 2,
'three' => 3,
'four' => 4,
'five' => 5,
'six' => 6,
'seven' => 7,
'eight' => 8,
'nine' => 9
);
echo $convert[strtolower($CASES_SOLD)]; // will display 3
If you only need the base 10 numerals, just make a map
$numberMap = array(
'ONE' => 1
, 'TWO' => 2
, 'THREE' => 3
// etc..
);
$number = $numberMap[$CASES_SOLD];
// $number == 3'
If you need something more complex, like interpreting Four Thousand Two Hundred Fifty Eight into 4258 then you'll need to roll up your sleeves and look at this related question.
Impress your fellow programmers by handling this in a totally obtuse way:
<?php
$text = 'four';
if(ereg("[[.$text.]]", "0123456789", $m)) {
$value = (int) $m[0];
echo $value;
}
?>
You need a list of numbers in english and then replace to string, but, you should play with 'thousand' and 'million' clause where must check if after string 'thousend-three' and remove integer from string.
You should play with this function and try change if-else and add some functionality for good conversion:
I'm writing now a simple code for basic, but you know others what should change, play!
Look at million, thousand and string AND, it should be change if no in string like '1345'. Than replace with str_replace each of them separaterly and join them to integer.
function conv($string)
{
$conv = array(
'ONE' => 1,
'TWO' => 2,
'THREE' => 3,
'FOUR' => 4,
'FIVE' => 5,
'SIX' => 6,
'SEVEN' => 7,
'EIGHT' => 8,
'NINE' => 9,
'TEN' => 10,
'ELEVEN' => 11,
'TWELVE' => 12,
'THIRTEEN' => 13,
'FOURTEEN' => 14,
'FIFTEEN' => 15,
'SIXTEEN' => 16,
'SEVENTEEN' => 17,
'EIGHTEEN' => 18,
'NINETEEN' => 19,
'TWENTY' => 20,
'THIRTY' => 30,
'FORTY' => 40,
'FIFTY' => 50,
'SIXTY' => 60,
'SEVENTY' => 70,
'EIGTHY' => 80,
'NINETY' => 90,
'HUNDRED' => 00,
'AND' => '',
'THOUSAND' => 000
'MILLION' => 000000,
);
if (stristr('-', $string))
{
$val = explode('-', $string);
#hardcode some programming logic for checkers if thousands, should if trim zero or not, check if another values
foreach ($conv as $conv_k => $conv_v)
{
$string[] = str_replace($conv_k, $conv_v, $string);
}
return join($string);
}
else
{
foreach ($conv as $conv_k => $conv_v)
{
$string[] = str_replace($conv_k, $conv_v, $string);
}
return join($string);
}
}
Basically what you want is to write a parser for the formal grammar that represents written numbers (up to some finite upper bound). Depending on how high you need to go, the parser could be as trivial as
$numbers = ('zero', 'one', 'two', 'three');
$input = 'TWO';
$result = array_search(strtolower($input), $numbers);
...or as involved as a full-blown parser generated by a tool as ANTLR. Since you probably only need to process relatively small numbers, the most practical solution might be to manually hand-code a small parser. You can take a look here for the ready-made grammar and implement it in PHP.
This is similar to Converting words to numbers in PHP
PHP doesn't have built in conversion functionality. You'd have to build your own logic based on switch statements or otherwise.
Or use an existing library like:
http://www.phpclasses.org/package/7082-PHP-Convert-a-string-of-English-words-to-numbers.html