PHP remove similar arrays based on several associations in an associative array - php

I have this type of associative array.
Array
(
[0] => Array
(
[bookedArea] => Comp Pool
[laneBooked] => 1
[paidBy] => Edmonton Aurora Synchronized Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
[1] => Array
(
[bookedArea] => Comp Pool
[laneBooked] => 2
[paidBy] => Edmonton Aurora Synchronized Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
[2] => Array
(
[bookedArea] => Comp Pool
[laneBooked] => 3
[paidBy] => Keyano Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
)
I would like to make a new array removing similar entries depending on several associations. startTime24hr, finishTime24hr bookedArea & paidBy if all 4 aren't the same then it shouldn't be removed. Plus I would like to capture the first and last lanes in the new associative array.
Array
(
[0] => Array
(
[bookedArea] => Comp Pool
[firstLaneBooked] => 1
[lastLaneBooked] => 2
[paidBy] => Edmonton Aurora Synchronized Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
[2] => Array
(
[bookedArea] => Comp Pool
[firstLaneBooked] => 3
[lastLaneBooked] => 3
[paidBy] => Keyano Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
)
I found this which would help me filter with only one association. However I need 3 more.
$copy = $array; // create copy to delete dups from the array you wish to filter from
$newFilteredArray = array(); // new filtered array.
$item1 = 'startTime24hr'; // the association you want to filter with.
for( $i=0; $i<count($array); $i++ ) {
if ( in_array( $array[$i][$item1], $newFilteredArray ) ) {
unset($copy[$i]);
}
else {
$newFilteredArray [] = $array[$i][$item1]
}
}
print_r($copy);
Edit ----
Thank you Alexander for helping me get the iterations correct.
I have been struggling with inserting into my filtered array the start and finish lanes. I have commented out my attempts to make it work. When its uncommented I get: Notice: Undefined index: startTime24hr and it seems like its in some infinite loop.
But if you comment my stuff no Notices. Can anyone help with this?
function array_push_assoc($array, $key, $value){
$array[$key][] = $value;
return $array;
}
$CPfilteredArray = array();
$lanes = 0;
for($i = 0; $i < count($compPoolArray); $i++) {
$add = true;
// $lanesAdded = false;
// $lanes = $lanes + 1;
// $startLane = $compPoolArray[$i]["laneBooked"];
for($j = 0; $j < count($CPfilteredArray); $j++) {
if(($compPoolArray[$i]["startTime24hr"] === $CPfilteredArray[$j]["startTime24hr"])
&& ($compPoolArray[$i]["finishTime24hr"] === $CPfilteredArray[$j]["finishTime24hr"])
&& ($compPoolArray[$i]["bookedArea"] === $CPfilteredArray[$j]["bookedArea"])
&& ($compPoolArray[$i]["paidBy"] === $CPfilteredArray[$j]["paidBy"])) {
$add = false;
// $lanes = $lanes + 1;
// $lanesAdded = True;
}
}
if($add) {
$CPfilteredArray[] = $compPoolArray[$i];
// if ($lanesAdded){
// $lastLane = $startLane + $lanes;
// $CPfilteredArray[$i] = array_push_assoc($CPfilteredArray, 'firstLane', $startLane);
// $CPfilteredArray[$i] = array_push_assoc($CPfilteredArray, 'lastLane', $lastLane);
// }
// else {
// $CPfilteredArray[$i] = array_push_assoc($CPfilteredArray, 'firstLane', $startLane);
// $CPfilteredArray[$i] = array_push_assoc($CPfilteredArray, 'lastLane', $startLane);
// }
}
}

Two loop cycles do the job.
$filteredArray = array();
for($i = 0; $i < count($array); $i++) {
$add = true;
for($j = 0; $j < count($filteredArray); $j++) {
if(($array[$i]["startTime24hr"] === $filteredArray[$j]["startTime24hr"])
&& ($array[$i]["finishTime24hr"] === $filteredArray[$j]["finishTime24hr"])
&& ($array[$i]["bookedArea"] === $filteredArray[$j]["bookedArea"])
&& ($array[$i]["paidBy"] === $filteredArray[$j]["paidBy"])) {
$add = false;
}
}
if($add) $filteredArray[] = $array[$i];
}
The resulting array called $filteredArray contains the filtered elements.

I know it doesn't really answer (not allowed to comment yet...) your question but if you're retrieving data from db, you could use a query to retrive grouped values.
It could look sth like this (pseudo-SQL)
SELECT
bookedArea
,paidBy
,startTime24hr
,finishTime24hr
,MIN(laneBooked)
,MAX(laneBooked)
FROM
reservation -- ??
WHERE
--condition
GROUP BY
bookedArea
,paidBy
,startTime24hr
,finishTime24hr

If you do know what elements you use to merge array value you may also use a hashmap to do the merge on insertion. So once you add an element to the array you create a hash over
$bookings = array();
$hash = md5($arr['bookedArea'] . $arr['paidBy'] . $arr['startTime24hr'] . $arr['finishTime24hr']);
if ( array_key_exists($hash, $bookings) ){
//add just another lane to the booking
}
else{
//create a new booking entry
}
that way your code may be even more efficient.

This may also work: (You can replace the serialize function to only serialize selected elements)
<?php
$arr=array(
0=>array("a"=>1, "b"=>"hello"),
1=>array("a"=>2, "b"=>"hellooo"),
2=>array("a"=>1, "b"=>"hello"),
);
function removeDuplicates( &$arr ){
$index=array();
$result=array();
foreach ($arr as $key=>$val){
$k=serialize($val);
if(!isset($index[$k])){
$index[$k]=$key;
$result[$key]=$val;
}
}
return $result;
}
$result=removeDuplicates($arr);
print_r($arr);
print '<br>';
print_r($result);
?>

Related

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++;}
}

Php: arrays nested loop performance

I have two arrays
$list = Array
([0] => stdClass Object
(
[id] => 10
[data] => "test data"
)
[1] => stdClass Object
...
...(max 3000 ~ 4000 items)
and
$attributes = Array
([0] => stdClass Object
(
[ids] => 11
[list] => '<ul>...</ul>'
)
[1] => stdClass Object
...
...(max 3000 ~ 4000 items)
I am trying to left join them but the only performing way I am being able to write for this to be usable is
$nrrowslist = count($list);
for ($i = 0; $i < $nrrowslist; $i++){
$nrat = count($attributes);
for ($j = 0; $j < $nrat; $j++)
{
if($list[$i]->id == $attributes[$j]->ids){
$list[$i]->attributes = $attributes[$j]->list;
array_splice($attributes, $j, 1); // remove the item
break; // since there is other item with that id
}
}
}
// completes in ~0.470 seconds
But if I write it
foreach($list as $art){
foreach($attributes as $attr){
if($art->id == $attr->ids){
$art->attributes = $attr->list;
}
}
}
it completes in ~ 5.500 seconds.. and is too much
what can I do to perform even more the first method situation?
You may have some success with two iterations and using the common parameter in your assignment
$newlist = array();
// I'm sure there's a better way to do this initial assignment
foreach($list as $row)
{
$newlist[$row->id] = $row;
}
foreach($attributes as $attr)
{
if(isset($newlist[$attr->ids]) === true)
{
$newlist[$attr->ids]->attributes = $attr->list;
}
}
var_dump($newlist);

Dynamic associative Array - list, count, sum, min, max

I've got an array with about 40 keys. I'd like to have a small function that returns a summary array.
Right now I've got the following that works:
foreach ($all_data as $value){
$new_array[ $value['location'] ][ $value['manufacturer'] ][ $value['model'] ] += 1;
}
This returns an array with everything I need. However, the location, manufacturer and model could be changed up for a bunch of other values.
what I am trying to do is have something simple as:
$new_array = summarize($all_data,array('location','manufacturer','model','count'),array('list','list','list','count') );}
where this summarize function would build the call. I think I just need a bit of help on how to get it to run the string as code for this array. Otherwise I get
$current_selection = "[ $row_item['location'] ][ $row_item['manufacturer'] ][ $row_item['model'] ]"
$return_array{$current_selection} += 1;
Where the end goal is to have a function like:
function summarize($data_array, $fields_array, $process_array){
//data_array = associative multi-dimensional data array
//fields = values to pull from the data_array
//process = array specifying whether to list, sum, count, average, max, min
$return_array = array();
$current_selection = "";
foreach($fields_array as $field){
$current_selection .= '[ $row_item[\'' . $field . '\'] ]';
}
foreach ($data_array as $row_item){
//dynamic = DOES NOT WORK
$return_array[$current_selection] += 1;//eval? create function? abstract?
//another attempt
${'return_array' . $current_selection} += 1;
//Manual = Does work
//$return_array[ $row_item['location'] ][ $row_item['manufacturer'] ][ $row_item['model'] ] += 1;
}
}
Thanks for any help on how to do an indirect reference.
JC
RESOLUTION
The final version that managed to resolve this looks like the following, thanks to user: check, for getting me on the correct path.
function summarize($data_array, $fields_array, $process_array){
$return_array = array();
$i = 0;
foreach ($data_array as $row){
$ii = 0;
$temp = array();
$temp2 = array();
foreach($fields_array as $key=>$field){
if($process_array[$ii] == 'list') $temp[$ii] = $row[$field];
if($process_array[$ii] == 'count') $temp2[$ii] = 1;
if($process_array[$ii] == 'sum') $temp2[$ii] = $row[$field];
$ii++;
}
$unique = true;
$ii = 0;
foreach($return_array as $row2){
if(array_intersect_key($row2,$temp) == $temp){//$row2 == $temp){
$unique = false;
break;
}
$ii++;
}
if($unique){
$return_array[$i] = $temp;
if(!empty($temp2)) $return_array[$i] = array_merge($temp,$temp2);
$i++;
}else{
if(!empty($temp2)){
foreach($temp2 as $key => $value){
if($process_array[$key] == 'sum') $temp2[$key] = $return_array[$ii][$key] + $value;
if($process_array[$key] == 'count') $temp2[$key] = $return_array[$ii][$key] + 1;
if($process_array[$key] == 'max') $temp2[$key] = ($return_array[$ii][$key] < $value) ? $value : $return_array[$ii][$key];
if($process_array[$key] == 'min') $temp2[$key] = ($return_array[$ii][$key] > $value) ? $value : $return_array[$ii][$key];
//TODO:(JC) 'average' - need to create a count field if not present (or always despite and assume overhead of extra computations).
// - then just calculate the 'sum' and divide by the counter as a last step before returning the array.
}
$return_array[$ii] = array_merge($temp,$temp2);
}
}
}
print_r($return_array);
return $return_array;
}
Which gives the following result:
/*
CALL: summarize($data,array('location','manufacturer','model','model','volume','colourvolume'),array('list','list','list','count','sum','sum') );
[0] = location
[1] = manufacturer
[2] = model
[3] = model count
[4] = mono volume sum
[5] = colour volume sum
*/
Array
(
[0] => Array
(
[0] =>
[1] => HP
[2] => LaserJet 4000
[3] => 3
[4] => 3000
[5] => 0
)
...
[17] => Array
(
[0] => Room 114
[1] => CANON
[2] => iR3235
[3] => 1
[4] => 4012
[5] => 0
)
[18] => Array
(
[0] => Room 115
[1] => LEXMARK
[2] => T652
[3] => 1
[4] => 20
[5] => 0
)
)
alternatively, if I assume that's $field_array contains sequentially key fields from root to sub key, you can loop your $field_array within $data_array loop
function summarize($data_array, $fields_array, $process_array){
$return_array = array();
foreach ($data_array as $row){
$temp = array();
foreach($fields_array as $key=>$field){
$temp = $key==0?$row[$field]:$temp[$field];
}
if(!empty($temp)) $return_array[] = $temp;
}
return $return_array;
}
and this is my array, will summarize with these function
$array = array(
array("multi"=>array("dimensional"=>array("array"=>"foo1"))),
array("multi"=>array("dimensional"=>array("array"=>"foo2"))),
array("multi"=>array("dimensional"=>array("array"=>"foo3"))),
array("multi"=>array("dimensional"=>array("array"=>"foo4"))),
array("multi"=>array("dimensional"=>array("array"=>"foo5"))),
array("multi"=>array("dimensional"=>array("array"=>"foo6"))),
array("multi"=>array("dimensional"=>array("array"=>"foo7"))),
array("multi"=>array("dimensional"=>array("array"=>"foo8"))),
array("multi"=>array("dimensional"=>array("array"=>"foo9")))
);
print_r(summarize($array,array("multi","dimensional","array"),NULL));
Ouput
Array ( [0] => foo1 [1] => foo2 [2] => foo3 [3] => foo4 [4] => foo5 [5] => foo6 [6] => foo7 [7] => foo8 [8] => foo9 )

PHP - how to compare value of arrays in dynamic way?

Ok, I have following 'challange';
I have array like this:
Array
(
[0] => Array
(
[id] => 9
[status] => 0
)
[1] => Array
(
[id] => 10
[status] => 1
)
[2] => Array
(
[id] => 11
[status] => 0
)
)
What I need to do is to check if they all have same [status].
The problem is, that I can have 2 or more (dynamic) arrays inside.
How can I loop / search through them?
array_diff does support multiple arrays to compare, but how to do it? :(
Which ever loop I have tried, or my Apache / browser died - or I got completely bogus data back.
You could just put the problem apart to make it easier to solve.
First get all status items from your array:
$status = array();
forach($array as $value)
{
$status[] = $value['status'];
}
You now have an array called $status you can see if it consists of the same value always, or if it has multiple values:
$count = array_count_values($status);
echo count($count); # number of different item values.
Try this code:
$status1 = $yourArray[0]['status'];
$count = count($yourArray);
$ok = true;
for ($i=1; $i<$count; $i++)
{
if ($yourArray[$i]['status'] !== $status1)
{
$ok = false;
break;
}
}
var_dump($ok);
function allTheSameStatus( $data )
{
$prefStatus = null;
foreach( $data as $array )
{
if( $prefStatus === null )
{
$prefStatus = $array[ 'status' ];
continue;
}
if( $prefStatus != $array[ 'status' ] )
{
return false;
}
}
return true;
}
I'm not sure what you want as output but you could iterate through the outer array and create an output array that groups the inner arrays by status:
$outputArray = array();
foreach ($outerArray as $an_array) {
$outputArray[$an_array['status']][] = $an_array['id'];
}

PHP Multidimensional Array problem

iam trying to build a multidimensional array.
public function saveRateTemplateData($RateTemplateInfo)
{
$RateTemplateID = $RateTemplateInfo['id'];
$Zones = $RateTemplateInfo['premium_zones'];
//$ZoneZipCodeIDs[] = array();
for ($n = 1; $n <= $RateTemplateInfo['premium_zones']; $n++) {
$ZoneNum = 'zone' . $n;
$ZipCodeArray = explode(",",$_POST[$ZoneNum]);
$ZipCodeIDs=array();
foreach ($ZipCodeArray as $v) {
$v = intval(trim($v));
if (strlen($v) == 5) {
array_push($ZipCodeIDs, $this->addZipCode($v));
} else {
echo "it isnt 5";
}
}
}
}
so what iam trying to do is make an array of an array. so this is how its supposed to look
Array
(
[1] => Array
(
[0] => 34
[1] => 31
[2] => 23
)
[2] => Array
(
[0] => 18
[1] => 4
[2] => 35
[3] => 1
)
)
i have tried numerous ways it doesnt work
basically i want it in this format VarName[ZoneNumbers][ZipCodeID]
so i can loop through it later on. so i can print like this $VarName[$n] then a array of all zipcodeID will print for Zone Number 1 in this case it will print 34,31,23
public function saveRateTemplateData($RateTemplateInfo)
{
$RateTemplateID = $RateTemplateInfo['id'];
$zones = array(); // you weren't using this so I'll use it to hold the data
for ($n = 1; $n <= $RateTemplateInfo['premium_zones']; $n++) {
$ZoneNum = 'zone' . $n;
// create an array under the zone number for holding the IDs
$zones[$n] = array();
$ZipCodeArray = explode(",",$_POST[$ZoneNum]);
foreach ($ZipCodeArray as $v) {
$v = (int) trim($v);
if (strlen($v) == 5) {
$zones[$n][] = $this->addZipCode($v);
} else {
// use exceptions for exceptional circumstances
throw new RuntimeException(sprintf('Invalid zone ID "%s"', $v));
}
}
}
return $zones;
}

Categories