Merge array elements within time range - how? - php

I have an array that contain user's activities on the website. It contains activities such as writing comments, news and groups. If two comments (or more) from different users have been written within an hour, I would like to gather those two arrays into one: User and 2 more commented on X. The code I have so far looks like this:
<?php
$output = array();
$output[] = array('userID' => 12, 'txt' => sprintf('%s commented in %s', 'User1', 'Event'), 'date' => 1393080072);
$output[] = array('userID' => 13, 'txt' => sprintf('%s commented in %s', 'User2', 'Event'), 'date' => 1393080076);
$output[] = array('userID' => 13, 'txt' => sprintf('%s created the news %s', 'User2', 'RANDOMNEWS'), 'date' => 1393080080);
$output[] = array('userID' => 14, 'txt' => sprintf('%s commented in %s', 'User3', 'Event'), 'date' => 1393080088);
$date = array();
foreach($output as $k => $d) {
$date[$k] = $d['date'];
}
array_multisort($date, SORT_DESC, $output);
print_r($output);
?>
So far the code above sorts the arrays by date (DESC). Desired result: one array: %s and 2 more commented in... and the other arrays removed from output. So by taking the latest comment and checking the date from the rest of the comments, it should be possible to handle this. I simply need some suggestions.
Thanks in advance

From what I understand from your question, I think you want to find out the number of users commenting in the last hour with respect to the latest commentor.
Using your logic, array_filter can help get those values which lie in the last hour.
This is the continuation of your code -
/*
...your code...
*/
$latest_time = $output[0]['date'];
$hour_past_time = $latest_time - 3600;
$user_ids = Array();
$res=array_values(
array_filter($output,function($arr)use($latest_time, $hour_past_time,&$user_ids){
if(
$arr["date"] <= $latest_time &&
$arr["date"] >= $hour_past_time &&
in_array($arr['userID'],$user_ids) == false
){
$user_ids[] = $arr['userID'];
return true;
}
}
)
);
echo "Users with their latest comments in the past hour- <br />";
var_dump($res);
$latest_user_id = "User".$res[0]['userID'];
$rest = count($res) - 1;
echo "<br />$latest_user_id and $rest more commented.<br />";
OUTPUT -
Users with their latest comments in the past hour-
array
0 =>
array
'userID' => int 14
'txt' => string 'User3 commented in Event' (length=24)
'date' => int 1393080088
1 =>
array
'userID' => int 13
'txt' => string 'User2 created the news RANDOMNEWS' (length=33)
'date' => int 1393080080
2 =>
array
'userID' => int 12
'txt' => string 'User1 commented in Event' (length=24)
'date' => int 1393080072
User14 and 2 more commented.
Hope this helps-

Related

PHP formatting Array to A New Array

Hi guys I am a beginner with PHP and want the best way in terms of performance to convert this array:
$old = array(
20 =>
array(
'name' => 'Heels',
'path' => '1/2/10/15/20',
),
15 =>
array(
'name' => 'Sandals',
'path' => '1/2/80/96/15',
),
10 =>
array(
'name' => 'Trainers',
'path' => '1/2/80/96/10',
),
);
To this:
$new = array(
20 =>
array(
'value' => 20,
'label' => 'Trainers > Sandals > Heels',
),
);
There is going to be loads of records surely exploding the paths and mapping them with the ids is going to slow it down in terms of performance just wondering whether there is a more efficient way if possible thanks.
If I understand correctly, you're trying to get the latest path relevant to each category and output it as a breadcrumb.
You can first sort the keys (ids) and then loop through the array creating the breadcrumb.
arsort($paths); # This gives the desired output in OP but makes more sense to use krsort() to sort DESC not ASC
$breadcrumb = (object) array (
'value' => array_keys($paths)[count($paths) - 1], # Get the highest id - if using krsort() use array_keys($paths)[0]
'labels' => implode(' > ', array_column($paths, 'name'));
);
# Update derived from The fourth bird's answer which skips the need for the foreach().
# Concept is to build an array of the labels to then make look pretty with the > effect
Here is a demo.
Output:
object (stdClass) (2) {
["value"] => int(20)
["labels"] => string(26) "Trainers > Sandals > Heels"
}
Another option could be to first create a mapper of the keys and the names. Then you could take the key from the mapper to create the path:
$result = [];
$mapper = array_combine(array_keys($old), array_column($old, 'name'));
foreach ($old as $key => $value) {
$path = implode(' > ', array_map(function($x) use ($mapper) {
return $mapper[(int)$x];
}, explode('/', $value['path'])));
$result[$key] = ['value' => $key,'label' => $path];
}
print_r($result);
Php demo
This is the hardcoded way, but i think you need to give a bit more information to get a dynamic solution.
<?php
$old = array(
20 =>
array(
'name' => 'Heels',
'path' => '1/2/10/15/20',
),
15 =>
array(
'name' => 'Sandals',
'path' => '1/2/80/96/15',
),
10 =>
array(
'name' => 'Trainers',
'path' => '1/2/80/96/10',
),
);
ksort($old);
$breadcrumbs = [];
$currentKey = 0;
foreach ( $old as $itemKey => $item) {
$currentKey = $itemKey;
$breadcrumbs[] = $item;
}
$new = [$currentKey] = [
'value' => $currentKey,
'label' => implode(' > ', $breadcrumbs)
];
printf($new);

sorting a multi dimensional array in php

I have an array of arrays, as such
$statuses = array(
[0] => array('id'=>10, 'status' => 'active'),
[1] => array('id'=>11, 'status' => 'closed'),
[2] => array('id'=>12, 'status' => 'active'),
[3] => array('id'=>13, 'status' => 'stopped'),
)
I want to be able to make a new array of arrays and each of those sub arrays would contain the elements based on if they had the same status.
The trick here is, I do not want to do a case check based on hard coded status names as they can be random. I want to basically do a dynamic comparison, and say "if you are unique, then create a new array and stick yourself in there, if an array already exists with the same status than stick me in there instead". A sample result could look something like this.
Ive really had a challenge with this because the only way I can think to do it is check every single element against every other single element, and if unique than create a new array. This gets out of control fast if the original array is larger than 100. There must be some built in functions that can make this efficient.
<?php
$sortedArray = array(
['active'] => array(
array(
'id' => 10,
'status' => 'active'
),
array(
'id' => 12,
'status' => 'active'
)
),
['closed'] => array(
array(
'id' => 11,
'status' => 'active'
)
),
['stopped'] => array(
array(
'id' => 13,
'status' => 'active'
)
),
)
$SortedArray = array();
$SortedArray['active'] = array();
$SortedArray['closed'] = array();
$SortedArray['stopped'] = array();
foreach($statuses as $Curr) {
if ($Curr['status'] == 'active') { $SortedArray['active'][] = $Curr; }
if ($Curr['status'] == 'closed') { $SortedArray['closed'][] = $Curr; }
if ($Curr['status'] == 'stopped') { $SortedArray['stopped'][] = $Curr; }
}
You can also do it with functional way though it's pretty the same like Marc said.
$sorted = array_reduce($statuses, function($carry, $status) {
$carry[$status['status']][] = $status;
return $carry;
}, []);

php array loop with sub loop

I would like to process an array in PHP to generate a new one.
The case is that I have an array with sales request and want to generate ticket request from that.
The issue I have is that depending on the type of sales request, 1 or more tickets are requered to be generated. If more tickets are requered, the only difference is the ticket number has a suffix of -1 or -2 added to the general ticket number.
I could do a foreach on the array and then a IF / ELSE if on the sales type and then set all the new array keys (which are all the same except for the ticket number)
But because I have many sales requests / lines this would be hard to maintain and I think not good for performance.
Example "semi" code:
$ticket = array ();
foreach ( $input as $k=>$v )
{
$ticket[$k] ['exp_date'] = date('Y-m-d', strtotime('+1 year', strtotime($input[$k] ['sales_date'])) );
$ticket[$k] ['ticket_number'] = input[$k] ['ticketnumber']; // if 1 day, ticket number, if 2 days ticket number + '-2' If multiple tickets are used, the first ticket should also have '-1'
$ticket[$k] ['ticket_type'] = $input[$k] ['product'];
$ticket[$k] ['sales_date'] = $input[$k] ['sales_date'];
$ticket[$k] ['sales_ticket_number'] = $input[$k] ['ticket_number'] ;
$ticket[$k] ['days'] = '0'; // not yet in use
$ticket[$k] ['days_remaining'] = '0'; // not yes in use
}
// if
// if ($input[$k] ['product'] == '1-day') { $loop is null}
// elseif ($input[$k] ['product'] == '2-days') { $loop is 2}
// elseif ($input[$k] ['product'] == '3-days') { $loop is 3}
Suggestions / tips are much appreciated !
/EDIT
Flow logic without code and sub-optimal:
foreach ($input as $k=>$v)
{
if ( $input[$k] ['product'] == '1-day')
{
create new lines in new array
}
else if ( $input[$k] ['product'] == '2-days')
{
loop 2 times
create same entries, but ticket number = ticketnumber-1 and second loop ticketnumber-2
}
From what I understand, you want something like this:
$ticket = array ();
foreach ( $input as $k=>$v )
{
// add the shared data for all tickets of one product to temporary ticket
$temp_ticket['exp_date'] = date('Y-m-d', strtotime('+1 year', strtotime($input[$k] ['sales_date'])) );
$temp_ticket['ticket_type'] = $input[$k]['product'];
$temp_ticket['sales_date'] = $input[$k]['sales_date'];
$temp_ticket['sales_ticket_number'] = $input[$k]['ticket_number'] ;
$temp_ticket['days'] = '0'; // not yet in use
$temp_ticket['days_remaining'] = '0'; // not yes in use
// get 'product' and retrieve the number of days
$days = (int) $input[$k]['product']; // '1-day' becomes 1, '2-days' becomes 2, ...
for ($d = 1; $d <= $days; $d++) {
// loop through the number of days and add to ticket array
// creates one ticket for each day of a product
$ticket[$k . '-' . $d] = $temp_ticket;
$ticket[$k . '-' . $d]['ticket_number'] = input[$k]['ticketnumber'] . '-' . $d;
}
}
Notes: $ticket[$k] (as in your code) can't be used multiple times since the data would be overwritten. That's why I used $ticket[$k . '-' . $d] to generate one ticket entry for each day of each sale. Since the rest of the ticket data seems to be the same, I can generate the temp ticket before and make copies in the for loop.
Example of input:
$input = array(
'order_1234' => array(
'sales_date' => '2013-12-31',
'product' => '1-day',
'ticket_number' => '1234',
),
'order_5678' => array(
'sales_date' => '2014-03-31',
'product' => '3-days',
'ticket_number' => '5678',
),
...
);
My code would produce output like this
$ticket = array(
'order_1234-1' => array(
'exp_date' => '2014-12-31',
'sales_date' => '2013-12-31',
'ticket_type' => '1-day',
'sales_ticket_number' => '1234',
'days' => '0',
'days_remaining' => '0',
'ticket_number' => '1234-1',
),
'order_5678-1' => array(
'exp_date' => '2015-03-31',
'sales_date' => '2014-03-31',
'ticket_type' => '3-days',
'sales_ticket_number' => '5678',
'days' => '0',
'days_remaining' => '0',
'ticket_number' => '5678-1',
),
'order_5678-2' => array(
'exp_date' => '2015-03-31',
'sales_date' => '2014-03-31',
'ticket_type' => '3-days',
'sales_ticket_number' => '5678',
'days' => '0',
'days_remaining' => '0',
'ticket_number' => '5678-2',
),
'order_5678-3' => array(
'exp_date' => '2015-03-31',
'sales_date' => '2014-03-31',
'ticket_type' => '3-days',
'sales_ticket_number' => '5678',
'days' => '0',
'days_remaining' => '0',
'ticket_number' => '5678-3',
),
...
);
If I understand the question correctly, which i may not have done you could do:
$prod = strpos($input[$k] ['product'], "-");
if ($prod == '1'):
$loop is null;
else:
$loop is $prod;
endif;
You can create an array holding all elements except the ticket number.
Then for each ticket you copy this array and add the ticket number.
Unfortunately I did not find a better way to copy a PHP array than this:
http://php.net/manual/de/arrayobject.getarraycopy.php

how can you merge arrays systematically without losing data?

so I'm trying to write a method that will merge all the arrays with a certain key value, but the problem I'm running into is that when I try and unset the array, so that there are not duplicate results it skips over several things, which is confusing me. so any advice on how I can improve this method would be greatly appreciated. the other question I have.. is there a way to check if each of these arrays have all 4 keys that I'm looking for.
'Release Date' =>
'Spreadsheet and Flyer Month' =>
'Advertise in Monthly Update' =>
'Feature in Catalog' =>
so what I'm doing is merging arrays with the same id in the db, so I don't have to do some really nasty SQL querys, but I'm wondering if there's a way of making sure that all 4 of these keys will be in every result... and if there is a value associated with one or however many.. my method will add the value to its key, and if there is no value associated with the key it will just make a empty string.
protected function array_with_same_val($array, $key) {
for($i = 0; $i < count($array); $i++) {
for($j = 1; $j < count($array); $j++) {
if(isset($array[$j]) && isset($array[$i]) && $array[$i][$key]==$array[$j][$key]) {
$temp = array_merge($array[$i], $array[$j]);
$array[$i] = $temp;
//unset($array[$j]);
}
}
}
return $array;
}
Here is a sample of my array (there will be a lot more values, this is just to give an idea):
'0' => array
(
'Release Date' => 'September 1, 2013',
'cp_id' => '112960'
),
'1' => array
(
'Spreadsheet and Flyer Month' => 'September 1, 2013',
'cp_id' => '112960'
),
'2' => array
(
'Advertise in Monthly Update' => 'September 1, 2013',
'cp_id' => '112960'
),
'3' => array
(
'Release Date' => 'September 1, 2013',
'cp_id' => '109141'
),
);
any help on this would be greatly appreciated.
Try this ($input is the array you provided above) ...
$output = array();
$requiredKeys = array('Release Date' => '', 'Spreadsheet and Flyer Month' => '', 'Advertise in Monthly Update' => '', 'Feature in Catalog' => '');
foreach ($input as $item) {
if (array_key_exists($item['cp_id'], $output)) {
$output[$item['cp_id']] = array_merge($output[$item['cp_id']], $item);
} else {
$output[$item['cp_id']] = array_merge($requiredKeys, $item);
}
}
$output = array_values($output);
The array_values call at the bottom is just to remove the string keys from the array.

PHP arrays: summing values with matching dates

i'm trying to figure out how to sum certain values of a multi-dimensional array if they have similar dates.
Here's my array:
<?$myArray=array(
array(
'year' => 2011,
'month ' => 5,
'day' => 13,
'value' => 2
),
array(
'year' => 2011,
'month '=> 5,
'day' => 14,
'value' => 5
),
array(
'year' => 2011,
'month ' => 5,
'day' => 13,
'value' => 1
),
array(
'year' => 2011,
'month ' => 5,
'day' => 14,
'value' => 9
)
);?>
here's how i'd like the output to look:
<?$output=array(
array(
'year' => 2011,
'month ' => 5,
'day' => 13,
'value' => 3 //the sum of 1+2
),
array(
'year' => 2011,
'month '=> 5,
'day' => 14,
'value' => 14 //the sum of 5+9
)
);?>
Notice how the 4 sub-arrays were matched on year/month/day and then only the value was summed. I've seen other SO threads on this topic but can't find one where only the value is summed and not the year/month/day values too.
Thoughts?
It may be easiest to initially index your output array with a combination of the year/month/day:
Note: Your example array above has all its month keys with a trailing space. I'm just using month here with no trailing space.
// Initialize output array...
$out = array();
// Looping over each input array item
foreach ($myArray as $elem) {
// Initialize a new element in the output keyed as yyyy-mm-dd if it doesn't already exist
if (!isset($out[$elem['year'] . "-" . $elem['month '] . "-" . $elem['day']])) {
$out[$elem['year'] . "-" . $elem['month '] . "-" . $elem['day']] = array(
// Set the date keys...
'year' => $elem['year'],
'month' => $elem['month '],
'day' => $elem['day'],
// With the current value...
'value' => $elem['value']
);
}
// If it already exists, just add the current value onto it...
else {
$out[$elem['year'] . "-" . $elem['month '] . "-" . $elem['day']]['value'] += $elem['value'];
}
}
// Now your output array is keyed by date. Use array_values() to strip off those keys if it matters:
$out = array_values($out);
Outputs (before calling array_values()):
array(2) {
'2011-5-13' =>
array(4) {
'year' =>
int(2011)
'month' =>
int(5)
'day' =>
int(13)
'value' =>
int(3)
}
'2011-5-14' =>
array(4) {
'year' =>
int(2011)
'month' =>
int(5)
'day' =>
int(14)
'value' =>
int(14)
}
}
Update:
To do the same thing with single-key dates (rather than 3-parts) it is easier without the concatenation:
$myArray=array(
array(
'date' => '2011-05-13',
'value' => 2
),
array(
'date' => '2011-05-14',
'value' => 5
),
array(
'date' => '2011-05-13',
'value' => 7
),
array(
'date' => '2011-05-14',
'value' => 3
),
);
foreach ($myArray as $elem) {
// Initialize a new element in the output if it doesn't already exist
if (!isset($out[$elem['date']])) {
$out[$elem['date'] = array(
// Set the date keys...
'date' => $elem['date'],
// With the current value...
'value' => $elem['value']
);
}
else {
$out[$elem['date']]['value'] += $elem['value'];
}
}
Here's how I would do it. The result will be in $newArray with datetime objects as keys. If you just want it as an indexed array it should be pretty easy to do.
// Example array
$myArray = array(
array(
'date' => new DateTime('1993-08-11'),
'value' => 3
),
array(
'date' => new DateTime('1993-08-11'),
'value' => 5
)
);
$newArray = array();
foreach($myArray as $element)
{
$iterationValue = $element['value'];
$iterationDate = $element['date'];
$dateKey = $iterationDate->format('Y-m-d');
if(array_key_exists($dateKey, $newArray))
{
// If we've already added this date to the new array, add the value
$newArray[$dateKey]['value'] += $iterationValue;
}
else
{
// Otherwise create a new element with datetimeobject as key
$newArray[$dateKey]['date'] = $iterationDate;
$newArray[$dateKey]['value'] = $iterationValue;
}
}
nl2br(print_r($newArray));
Actually ended up doing the pretty much the same thing as #MichaelBerkowski solution. Still, having DateTime objects is always more flexible when you wan't to do things with the dates later in your application.
Edit: Now tested it and fixed errors

Categories