Looping through array and get non-repeating values - php

I have an array of elements which I queried from the mongoDB.
This array has the id of a device and value of this device's consumptions.
For example there are 3 different ids -> 18,5,3 and multiple mixed values.
// first record of 18 so get value.
$row[0]["id"] = 18;
$row[0]["value"] = 100;
// not first record of 18 so ignore and move to the next record
$row[1]["id"] = 18;
$row[1]["value"] = 40;
// first record of 5 so get value.
$row[2]["id"] = 5;
$row[2]["value"] = 20;
// not first record of 18 so ignore and move to the next record
$row[3]["id"] = 18;
$row[3]["value"] = 30;
// first record of 3 so get value.
$row[4]["id"] = 3;
$row[4]["value"] = 20;
//not first record of 5 so ignore and move to the next record**
$row[5]["id"] = 5;
$row[5]["value"] = 30;
// not first record of 18 so ignore and move to the next record
$row[6]["id"] = 18;
$row[6]["value"] = 10;
...
....
What I am trying to do is loop through this $row array and get the most recent value of the id.
For example in above example what I want to return is:
id value
18 100
5 20
3 20
How can I do that kind of logic?

It can be done in several ways. The easiest one is to use a foreach:
$result = array();
foreach ($row as $i) {
if (! array_key_exists($i['id'], $result)) {
$result[$i['id']] = $i['value'];
}
}
# Verify the result
print_r($result);
The output is:
Array
(
[18] => 100
[5] => 20
[3] => 20
)
The same processing, but using array_reduce():
$result = array_reduce(
$row,
function(array $c, array $i) {
if (! array_key_exists($i['id'], $c)) {
$c[$i['id']] = $i['value'];
}
return $c;
},
array()
);

If you want to keep only the first occurrence of each 'id' then just add the values to an aggregate array - but only if they haven't been added already. Then grab the values of the aggregate array.
https://tehplayground.com/NRvw9uJF615oeh6C - press Ctrl+Enter to run
$results = array();
foreach ($row as $r) {
$id = $r['id'];
if (! array_key_exists($id, $results)) {
$results[$id] = $r;
}
}
$results = array_values($results);
print_r($results);
Array
(
[0] => Array
(
[id] => 18
[value] => 100
)
[1] => Array
(
[id] => 5
[value] => 20
)
[2] => Array
(
[id] => 3
[value] => 20
)
)

Try this
$alreadyfound = []; // empty array
$result = [];
foreach ($row as $item)
{
if (in_array($item["id"], $alreadyfound))
continue;
$alreadyfound[] = $item["id"]; // add to array
$result[] = $item;
}
The result
Array
(
[0] => Array
(
[id] => 18
[value] => 100
)
[1] => Array
(
[id] => 5
[value] => 20
)
[2] => Array
(
[id] => 3
[value] => 20
)
)

The array_unique() function is exactly what you are looking at.
See the documentation here : array_unique() documentation

Using array_column with an index key will almost do it, but it will be in the reverse order, so you can reverse the input so that it works.
$result = array_column(array_reverse($row), 'value', 'id');

Related

PHP unshif and push value based on current array keys

I have a quiz where you can use text field or radio buttons for the answers. The answers are stored in a database. None of the answers are required so people can skip questions.
Each entry is stored in a row:
Array
(
[0] => Array
(
[0] => Dave
[1] => ok
[2] => Manchester
[3] => No
)
[1] => Array
(
[0] => James
[1] => Happy
[2] => London
[3] => Yes
)
[2] => Array
(
[0] => Victoia
[2] => Leeds
)
)
Currently the question number is represented by the key. So Victoria hasn't answered question 1 or 3. My aim is to add the unanswered into the array witht the correct key value being null.
Here is my code so far but I'm struggling to get the array key position correct:
$answersArr = (array) $answers;
$row = array();
$items = array();
$numberOfQuesitons = count($headers);
foreach ($answersArr as $key=>$result) {
$answer = json_decode(stripslashes($result->answers));
$row[$key] = (array) $answer;
$single = count($row[$key]);
$currentKey = key($row[$key]);
for ($i = $single ; $i < $numberOfQuesitons; $i++) {
if ($numberOfQuesitons - $i > 0) {
if ($currentKey > 0) {
array_unshift($row[$key], null);
} else {
array_push($row[$key], null);
}
}
}
}
print_r($row);
The out put I get:
Array
(
[0] => Array
(
[0] => Admin
[1] => ok
[2] => Manchester
[3] => No
)
[1] => Array
(
[0] => Rod
[1] => Happy
[2] => London
[3] => Yes
)
[2] => Array
(
[0] => Rozi
[2] => Leeds
[3] =>
[4] =>
)
)
The last row in the array needs to look like this:
[2] => Array
(
[0] => Victoia
[1] =>
[2] => Somewhere
[3] =>
)
A little stuck here any help would be much appreciated.
Thanks
Let me say that I read through your code and have to make two assumptions: $numberOfQuesitons = 4 and $single = 2 in the case of Victoia.
Assumption is based on the fact that the name of a person is included in the result arrays and otherwise could not be derived.
So in your code at the iteration of Victoia we will have the following array to work with:
$row[$key] = [
0 => 'Victoia',
2 => 'Leeds',
];
Then at the inner for loop the following will happen (as noted in comments):
for ($i = $single; $i < $numberOfQuesitons; $i++) {
// 4 - 2 = 2, 2 > 0 = true <-- first iteration
// 4 - 3 = 1, 1 > 0 = true <-- second (and final) iteration
if ($numberOfQuesitons - $i > 0) {
// $currentKey doesn't change in this process
// and since the key is taken from the array
// $row[$key] points to, the key function will return 0 each iteration.
if ($currentKey > 0) {
// Also note that unshift will only add elements to the front of the array.
array_unshift($row[$key], null);
} else {
// Hence we drop down in this branch of the if statemtent
// as 0 > 0 evaluates to false each evaluation.
// So we start with [0] => Rozi || Victoia
// [2] => Leeds || Somewhere
// Push null thus: [3] => null
// Push null thus: [4] => null
// Hence our final result.
// Also note that push only 'pushes' elements at the end of the array.
array_push($row[$key], null);
}
}
}
To solve this we could change the logic a little bit, but personally I would modify the nested loop to the following (or something similar):
$answersArr = (array) $answers;
$row = array();
$numberOfQuesitons = count($headers);
foreach ($answersArr as $key => $result) {
$answer = json_decode(stripslashes($result->answers));
$row[$key] = (array) $answer;
// We simply create a range from 0 up to the last question number.
$all = range(0, $numberOfQuesitons - 1);
// Taking the difference between all questions and the answered
// ones will give us the missing ones.
$unAnsweredQs = array_diff($all, array_keys($row[$key]));
// Add those missing questions to the array.
foreach ($unAnsweredQs as $unAnswered) {
// Or maybe a more appropriate default.
$row[$key][$unAnswered] = null;
}
// Sort the keys, such that we respect the wanted order
// Name (key 0), Q1, Q2, ..., QN
ksort($row[$key]);
}
print_r($row);

Create new array inside a multidimensional array, step up name each cycle

Question:
How can I iterate below so it checks existence of key "round_1", next script run it should check existense of key "round_2", etc. Everytime it would encounter that the key is missing it should create the key.
It is working with "round_1" as expected.
<?php
// Create array skeleton.
$array_skeleton = array_fill(1, 3, "");
print_r($array_skeleton);
// Populate the skeleton with random numbers, values [1 to 6].
foreach($array_skeleton as $key => $value) {
$populated_array[$key] = random_int(1, 6);
};
print_r($populated_array);
// Create empty array for purpose to become multidimensional array.
$scorecard = [];
// Check if [round_1] is missing, if so create [round_1] and populate it.
if(!array_key_exists("round_1", $scorecard)) {
echo "round_1 is missing, creating it";
$scorecard["round_1"] = $populated_array;
}
print_r($scorecard);
Outcome works fine as expected, after first script run:
(
[round_1] => Array
(
[1] => 3
[2] => 4
[3] => 1
)
)
Expected outcome, after second script run:
Note! It is correct that the values would be different per each round since they are randomly created.
(
[round_1] => Array
(
[1] => 3
[2] => 4
[3] => 1
)
[round_2] => Array
(
[1] => 1
[2] => 4
[3] => 2
)
)
I think your entire code can be simplify:
first define function for create array with random number:
function createRandomNumberArray($numOfElem, $maxRange) {
for ($i = 0; $i < $numOfElem; $i++)
$res[] = random_int(1, $maxRange);
return $res;
}
Second, assuming your keys are made of "round_#INT#" pattern you can do
$biggest = max(array_map(function ($e) {$p = explode("_", $e); return $p[1];}, array_keys($arr)));
And now do:
$newKey = "round_" . ($biggest + 1);
$scorecard[$newKey] = createRandomNumberArray(3,6);
Reference: array-map, explode, max, random-int

Split array but sum of each array not to exceed a max value else push to next array index

I have two arrays, Array 1 being a donor array which has a series of values (not necessarily equal values as in my example). Array 2 is the desired result and would store a series of sub arrays with values from Array 1 where each sub array's total sum would not exceed 25. If it does, the excess would get pushed to the next index in Array 2 where the rules would also apply.
Donor array (Array 1):
$a1=array(10,10,10,10,10,10,10,10,10,10);
Desired output (Array 2):
Array
(
[0] => 10,10,5
[1] => 5,10,10
[2] => 10,10,5
[3] => 5,10,10
)
Here the code I tried but it gets an error:
Notice: Undefined offset: 10...etc.
$a1=array(10,10,10,10,10,10,10,10,10,10);
$a2=array();
$count=count($a1);
for($i=0;$i<$count;$i++){
$a2count=array_sum($a2);
if($a2count>25){
$i=$i+1;
$a2[$i]=$a1[$i];
}
else{
$a2[$i]=$a1[$i];
}
}
print_r($a2);
I don't know what logic to implement and get result I'm looking for.
Mayhaps something like this will work for you. I will notate so it's not just a copy-and-paste answer. Maybe someone will get some insight on it to improve it in the future:
function slitArray($a1,$num = 25)
{
# Used to store the difference when the value exceeds the max
$store = 0;
# Storage container that will be built using sums/diffs
$new = array();
# Loop through the main array
foreach($a1 as $value) {
# If the last key/value pair in our return array is an array
if(is_array(end($new)))
# Add up the values in that array
$sum = array_sum(current($new));
else
# If not array, no values have been stored yet
$sum = 0;
# This just gets the last key
$count = (count($new)-1);
# Assign last key
$i = ($count <= 0)? 0 : $count;
# If the sum of the current storage array plus the value
# of the current array is greater than our max value
if(($sum + $value) > $num) {
# Take max and remove the current total of storage array
$use = ($num-$sum);
# Take what's left and remove it from the current value
$store = ($value-$use);
# If the current stored value (the value we want to push to
# the next storage k/v pair) is more than the max allowed
if($store > $num) {
# Takes a value, checks if it's greater than max,
# and if it is, breaks the value up by max as a divider
$divide = function($store,$num)
{
if($store > $num) {
$count = ceil($store/$num);
for($i=0; $i<$count; $i++) {
$new[] = ($store > $num)? $num : $store;
$store -= $num;
}
return $new;
}
else
return array($store);
};
# This should either be an array with 1 or more values
$forward = $divide($store,$num);
# Do a look forward and add this excess array into our
# current storage array
$a = $i;
foreach($forward as $aVal) {
$new[$a+=1][] = $aVal;
}
}
# If the store value is less than our max value, just add
# it to the next key in this storage array
else {
$new[$i+1][] = $store;
# Reset the storage back to 0, just incase
$store = 0;
}
}
# Set the current "use" value as the current value in our
# from-array. Since it doesn't exceed the max, it just gets
# added to the storage array
else
$use = $value;
# Sometimes the math makes $use 0, keep that out of the
# storage array. The $use value is the current value to add at
# the time of iteration. Previous storage values are added as
# future-keys
if($use > 0)
$new[$i][] = $use;
}
# Return the final assembled array
return $new;
}
# To use, add array into function
$a1 = array(10,10,10,10,10,10,10,10,10,10);
# to split using different max value, just add it to second arg
# example: slitArray($a1,20);
print_r(slitArray($a1));
Gives you:
Array
(
[0] => Array
(
[0] => 10
[1] => 10
[2] => 5
)
[1] => Array
(
[0] => 5
[1] => 10
[2] => 10
)
[2] => Array
(
[0] => 10
[1] => 10
[2] => 5
)
[3] => Array
(
[0] => 5
[1] => 10
[2] => 10
)
)
An array input of:
$a1 = array(23,2,71,23,50,2,3,4,1,2,50,75);
Gives you:
Array
(
[0] => Array
(
[0] => 23
[1] => 2
)
[1] => Array
(
[0] => 25
)
[2] => Array
(
[0] => 25
)
[3] => Array
(
[0] => 21
[1] => 4
)
[4] => Array
(
[0] => 19
[1] => 6
)
[5] => Array
(
[0] => 25
)
[6] => Array
(
[0] => 19
[1] => 2
[2] => 3
[3] => 1
)
[7] => Array
(
[0] => 3
[1] => 1
[2] => 2
[3] => 19
)
[8] => Array
(
[0] => 25
)
[9] => Array
(
[0] => 6
[1] => 19
)
[10] => Array
(
[0] => 25
)
[11] => Array
(
[0] => 25
)
[12] => Array
(
[0] => 6
)
)
Here you go: The logic is not so hard. Hope it helps.
<?php
$a1=array(10,10,10,10,10,10,10,10,10,10);
$a2 = [];
$a3 = [];
$m = 0;
for($i = 0; $i < count($a1); ++$i){
$m += $a1[$i];
if($m > 25){
$n = $m % 25;
if(array_sum($a2) != 25){
$a2[] = $n;
}
$a3[] = implode(',', $a2);
$a2 = [];
$m = $n;
$a2[] = $n;
} else{
$a2[] = $a1[$i];
}
}
$a3[] = implode(',', $a2);
print_r($a3);
?>
The final code for this question
<?php
function slitArray($a1,$num = 25)
{
$store = 0;
$new = array();
foreach($a1 as $value) {
if(is_array(end($new)))
$sum = array_sum(current($new));
else
$sum = 0;
$count = (count($new)-1);
$i = ($count <= 0)? 0 : $count;
if(($sum + $value) > $num) {
$use = ($num-$sum);
$store = ($value-$use);
if($store > $num) {
$divide = function($store,$num)
{
if($store > $num) {
$count = ceil($store/$num);
for($i=0; $i<$count; $i++) {
$new[] = ($store > $num)? $num : $store;
$store -= $num;
}
return $new;
}
else
return array($store);
};
$forward = $divide($store,$num);
$a = $i;
foreach($forward as $aVal) {
$new[$a+=1][] = $aVal;
}
}
else {
$new[$i+1][] = $store;
$store = 0;
}
}
else
$use = $value;
if($use > 0)
$new[$i][] = $use;
}
return $new;
}
$a1 = array(10,20,30,40,50,60);
$arr=slitArray($a1);
print_r($arr);
?>
Let me help you a bit using Pseudocode:
ar1 = {10,10,10,20,40,[0]=>1,[0]=>3,[0]=>4};
ar2 = new array (ar.length) \\ worst case
int c = 0; \\current
foreach (ar1 as $value){
ar2 [c]+=ar1[i];
if (ar2 [c]>25){ c++;}
}
Logic behind the code:
Add the value of ar1[i] to the current ar2 value till it passes your limit (25 in this case). If it exceeds you boundary, than move to the next value in the goal array. The worst case would be, that every value is over 25, so it would be an exact copy of the original array.
Here the php code:
$ar1=array(10,10,10,10,10,10,10,10,10,10);
$ar2 = array(0,0,0,0,0,0,0,0,0,0);
$c = 0;
foreach( $ar1 as $key => $value ){
$ar2[$c]=$value+$ar2[$c];
if ($ar2[$c]>25){$c++;}
}

Create an array from two array PHP

i have two arrays
$value_array = array('50','40','30','20','10');
$customer = array('300','200','100');
i want to distribute the value array to customers based on the value of customers that is taken as limit.adding values by checking it wont cross the limit that is 300 , 200 and 100.
but customer array not working one direction it should work first forward and then backward like that
i want to produce an array in form of
Array
(
[0] => Array
(
[0] => 50
)
[1] => Array
(
[0] => 40
[1] => 10
)
[2] => Array
(
[0] => 30
[1] => 20
)
)
After completing customer loop first time it should start from last to first. both array count will change , i mean count.
value array should check 50 -> 300 , 40->200, 30->100 then from last ie, 20 ->100, 10->200 etc.
I tried like
$i = 0;
while($i < count($customer)){
foreach($value_array as $k=>$value){
$v = 0;
if($value <= $customer[$i]){
$customer2[$i][] = $value;
unset($value_array[$k]);
$v = 1;
}
if($v ==1){
break;
}
}
//echo $i."<br/>";
if($i == (count($customer)-1) && (!empty($value_array))){
$i = 0;
$customer = array_reverse($customer, true);
}
$i++;
}
echo "<pre>";
print_r($customer2);
$valueArray = array('50','40','30','20','10','0','-11');
$customer = array('300','200','100');
function parse(array $valueArr, array $customerArr)
{
$customerCount = count($customerArr);
$chunkedValueArr = array_chunk($valueArr, $customerCount);
$temp = array_fill(0, $customerCount, array());
$i = 0;
foreach ($chunkedValueArr as $item) {
foreach ($item as $key => $value) {
$temp[$key][] = $value;
}
$temp = rotateArray($temp);
$i++;
}
// if $i is odd
if ($i & 1) {
$temp = rotateArray($temp);
}
return $temp;
}
function rotateArray(array $arr)
{
$rotatedArr = array();
//set the pointer to the last element and add it to the second array
array_push($rotatedArr, end($arr));
//while we have items, get the previous item and add it to the second array
for($i=0; $i<sizeof($arr)-1; $i++){
array_push($rotatedArr, prev($arr));
}
return $rotatedArr;
}
print_r(parse($valueArray, $customer));
returns:
Array
(
[0] => Array
(
[0] => 50
[1] => 0
[2] => -11
)
[1] => Array
(
[0] => 40
[1] => 10
)
[2] => Array
(
[0] => 30
[1] => 20
)
)

Breaking an array into groups based on values

Using PHP, I'm trying to break an array up into multiple arrays based on groups of values. The groups are based on the values being between 1 and 5. But here's the hard part...
I need to loop through the array and put the first set of values that are between 1 and 5 in their own array, then the next set of values that are between 1 and 5 in their own array, and so on.
But each group WON'T always include 1,2,3,4,5. Some groups could be random.
Examples:
1,1,2,2,3,4,5 - this would be a group
1,2,3,4,4,4 - this would be a group
1,2,3,3,5 - this would be a group
2,2,3,3,5 - this would be a group
So I can't just test for specific numbers.
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 1
[6] => 2
[7] => 3
[8] => 4
[9] => 4
[10] => 1
[11] => 1
[12] => 3
[13] => 4
[14] => 5
)
Any Ideas?
I would just check if the current value is larger than the previous value, and if yes, begin a new group.
$groups = array();
$groupcount = 1;
foreach( $array as $key=>$value )
{
if( $key > 0 ) // there's no "previous value" for the first entry
{
if( $array[$key] < $array[$key-1] )
{
$groupcount = $groupcount + 1;
}
}
$group[groupcount][] = $value;
}
Is this what you are looking for?
$groups = array();
$cur = array();
$prev = 0;
foreach ($numbers as $number)
{
if ($number < $prev)
{
$groups[] = $cur;
$cur = array();
}
$cur[] = $number;
$prev = $number;
}
if ($cur) $groups[] = $cur;
Untested. (Edit: corrected some obvious mistakes.)

Categories