how can you merge arrays systematically without losing data? - php

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.

Related

Getting multiple specific key value pairs if exist in associate array

I want to convert my Associate Array into array based on certain criteria:
It should contain multiple column (Hence, can't user array_column in this case.)
It should ADD duplicate date
It should remove particular sub-array if NULL. (array_filter)
Input :
array (
0 =>
array (
'date' => "2019-03-31",
'a' => '1',
'b' => '1',
),
1 =>
array (
'date' => "2019-04-02", // If "SAME" date then ADD them
'a' => '1',
'b' => '1',
),
2 =>
array (
'date' => "2019-04-02", // If "SAME" date then ADD them
'a' => '2',
'b' => '1',
),
3 =>
array (
'date' => "2019-04-10",
'a' => '', // If "NULL" then remove the particular sub-array
'b' => '1',
),
4 =>
array (
'date' => "2019-04-18",
'a' => '4',
'b' => '10',
),
)
I've tried the following,
Although I was able to select multiple column in array but this is giving me:
"Duplicate" date.
And including sub-array which has "Null" value.
function colsFromArray(array $array, $keys)
{
if (!is_array($keys)) $keys = [$keys];
return array_map(function ($el) use ($keys) {
$o = [];
foreach($keys as $key){
$o[$key] = isset($el[$key])?$el[$key]:false;
}
return $o;
}, $array);
}
$result = colsFromArray($arr, array("date", "a"));
Required Output :
array (
0 =>
array (
'date' => "2019-03-31",
'a' => '1',
),
1 =>
array (
'date' => "2019-04-02",
'a' => '3',
),
2 =>
array (
'date' => "2019-04-18",
'a' => '4',
),
)
You could try something like this:
function colsFromArray(array $array, $keys)
{
if (!is_array($keys)) $keys = [$keys];
$returnArray = [];
foreach($array as $in){
foreach($keys as $key){
//Check if the key already has a value
if(isset($returnArray[$in['date']][$key]) && is_numeric($returnArray[$in['date']][$key])){
$returnArray[$in['date']][$key] += $in[$key];
}
//If the key is empty, continue
elseif(empty($in[$key])){
continue;
}
//If value is to be set, set date as well
else{
$returnArray[$in['date']]['date'] = $in['date'];
$returnArray[$in['date']][$key] = $in[$key];
}
}
}
//Return array after resetting indexes and removing empty entries
return array_values(array_filter($returnArray));
}
The way that you are passing in the array of columns to your customer function is ambiguous about what should be used to group by and what should be summed. OR if you will never have more than two elements in your 2nd parameter, then your custom function lacks flexibility.
Notice how #SverokAdmin's answer has date hardcoded in the logic, but there is no guarantee that this column name will always exist in the first parameter $array. Ergo, the utility function cannot be reliably used as a utility function.
Consider this custom function which provides your desired result, but allows more meaningful parameter declarations. My snippet allows you to group by one or more column, keep one or more columns in the subarrays, and specify which column should be summed. I cannot make the last column iterable because you state that if a column is missing a value, it should be omitted from the result. In other words, your desired result is unknown if you wanted to sum a and b, but one of the a values was empty.
Code: (Demo)
function colsFromArray(array $array, $groupBy, $keepables, $summable): array
{
$groupBy = (array)$groupBy;
$keepables = (array)$keepables;
// cannot make $summable iterable type because question is unclear on the desired outcome when one of the columns is empty
return array_values(
array_reduce(
$array,
function ($result, $row) use($groupBy, $keepables, $summable) {
$uniqueKey = [];
foreach ($groupBy as $col) {
$uniqueKey[] = $row[$col];
}
$uniqueKey = implode('_', $uniqueKey);
if (!$row[$summable]) {
return $result;
}
foreach ($keepables as $keepable) {
$result[$uniqueKey][$keepable] = $row[$keepable];
}
$result[$uniqueKey][$summable] = ($result[$uniqueKey][$summable] ?? 0) + $row[$summable];
return $result;
},
[]
)
);
}
var_export(
colsFromArray($arr, 'date', 'date', 'a')
);
Output:
array (
0 =>
array (
'date' => '2019-03-31',
'a' => 1,
),
1 =>
array (
'date' => '2019-04-02',
'a' => 3,
),
2 =>
array (
'date' => '2019-04-18',
'a' => 4,
),
)

Cheapest way to get size of multidimensional PHP array

I have a multidimensional array. If it contains less than a threshold $threshold = 1000 bits/bytes/anything of data in total, including it's keys, I want to fetch more content.
What is the cheapest way, performance/memory wise, to get an approximate of the array size?
Right now, I use strlen(serialize($array)). Updated as per the comments:
$threshold = 1000;
$myArray = array(
'size' => 0,
'items' => array(
['id' => 1, 'content' => 'Lorem ipsum'],
['id' => 2, 'content' => 'Dolor sit'],
['id' => 3, 'content' => 'Amet']
)
);
while($myArray['size'] < $threshold)
{
echo "Array size is below threshold.<br/>";
addStuffToArray($myArray);
}
function addStuffToArray(&$arr)
{
echo "Adding stuff to array.<br/>";
$newArrayItem = array(
'id' => rand(0, 10000),
'content' => rand(999999, 999999)
);
$arr['size'] += strlen(serialize($newArrayItem));
$arr['items'][] = $newArrayItem;
}
PHPFiddle here.
You could always just count:
$count = array_map('count', $myArray);
This will give you an array with the count of all the sub-arrays.
However, the way you have done it is not bad.

Merge array elements within time range - how?

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-

Check the 'form' of a multidimensional array for validation [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
recursive array_diff()?
I have a static multidimensional array which is always going to be in the same form. E.g. it will have the same keys & hierarchy.
I want to check a posted array to be in the same 'form' as this static array and if not error.
I have been trying various methods but they all seem to end up with a lot of if...else components and are rather messy.
Is there a succinct way to achieve this?
In response to an answer from dfsq:
$etalon = array(
'name' => array(),
'income' => array(
'day' => '',
'month' => array(),
'year' => array()
),
'message' => array(),
);
$test = array(
'name' => array(),
'income' => array(
'day' => '',
'month' => array(),
'year' => array()
),
'message' => array(),
);
// Tests
$diff = array_diff_key_recursive($etalon, $test);
var_dump(empty($diff));
print_r($diff);
And the results from that are
bool(false)
Array ( [name] => 1 [income] => Array ( [month] => 1 [year] => 1 ) [message] => 1 )
Author needs a solution which would test if the structure of the arrays are the same. Next function will make a job.
/**
* $a1 array your static array.
* $a2 array array you want to test.
* #return array difference between arrays. empty result means $a1 and $a2 has the same structure.
*/
function array_diff_key_recursive($a1, $a2)
{
$r = array();
foreach ($a1 as $k => $v)
{
if (is_array($v))
{
if (!isset($a2[$k]) || !is_array($a2[$k]))
{
$r[$k] = $a1[$k];
}
else
{
if ($diff = array_diff_key_recursive($a1[$k], $a2[$k]))
{
$r[$k] = $diff;
}
}
}
else
{
if (!isset($a2[$k]) || is_array($a2[$k]))
{
$r[$k] = $v;
}
}
}
return $r;
}
And test it:
$etalon = array(
'name' => '',
'income' => array(
'day' => '',
'month' => array(),
'year' => array()
),
'message' => ''
);
$test = array(
'name' => 'Tomas Brook',
'income' => array(
'day' => 123,
'month' => 123,
'year' => array()
)
);
// Tests
$diff = array_diff_key_recursive($etalon, $test);
var_dump(empty($diff));
print_r($diff);
This will output:
bool(false)
Array
(
[income] => Array
(
[month] => Array()
)
[message] =>
)
So checking for emptiness of $diff array will tell you if arrays have the same structure.
Note: if you need you can also test it in other direction to see if test array has some extra keys which are not present in original static array.
You could user array_intersect_key() to check if they both contain the same keys. If so, the resulting array from that function will contain the same values as array_keys() on the source array.
AFAIK those functions aren't recursive, so you'd have to write a recursion wrapper around them.
See User-Notes on http://php.net/array_diff_key
Are you searching for the array_diff or array_diff_assoc functions?
use foreach with the ifs...if u have different tests for the different inner keys eg
$many_entries = ("entry1" = array("name"=>"obi", "height"=>10));
and so on, first define functions to check the different keys
then a foreach statement like this
foreach($many_entries as $entry)
{
foreach($entry as $key => $val)
{
switch($key)
{
case "name": //name function
//and so on
}
}
}

How to extract data out of a specific PHP array

I have a multi-dimensional array that looks like this:
The base array is indexed based on category ids from my catalog.
$cat[category_id]
Each base array has three underlying elements:
['parent_id']
['sort_order']
['name']
I want to create a function that allows us to create a list of category_id's and names for a given parent_category_id in the correct sort order. Is this possible? Technically it is the same information, but the array is constructed in a weird way to extract that information.
Here is an example definition for the array:
$cat = array();
$cat[32]['parent_id']= 0;
$cat[32]['sort_order']= 1;
$cat[32]['name']= 'my-category-name1';
$cat[45]['parent_id']= 0;
$cat[45]['sort_order']= 0;
$cat[45]['name']= 'my-category-name2';
$cat[2]['parent_id']= 0;
$cat[2]['sort_order']= 2;
$cat[2]['name'] = "my-category-name3";
$cat[3]['parent_id']= 2;
$cat[3]['sort_order']= 1;
$cat[3]['name'] = "my-category-name4";
$cat[6]['parent_id']= 2;
$cat[6]['sort_order']= 0;
$cat[6]['name'] = "my-category-name5";
Assuming it's something of this sort:
$ary = Array(
0 => Array(
'parent_category_id' => null,
'sort_order' => 0,
'name' => 'my-category-name0'
),
1 => Array(
'parent_category_id' => 0,
'sort_order' => 1,
'name' => 'my-category-name1'
),
2 => Array(
'parent_category_id' => 0,
'sort_order' => 2,
'name' => 'my-category-name2'
),
3 => Array(
'parent_category_id' => null,
'sort_order' => 0,
'name' => 'my-category-name3'
),
4 => Array(
'parent_category_id' => 3,
'sort_order' => 0,
'name' => 'my-category-name4'
)
);
You can use a combination of a foreach and usort to achieve what you're going for.
// #array: the array you're searchign through
// #parent_id: the parent id you're filtering by
function getFromParent($array, $parent_id){
$result = Array();
foreach ($array as $category_id => $entry){
if ($entry['parent_category_id']===$parent_id)
$result[$category_id] = $entry;
}
usort($result,create_function('$a,$b','return ($a["sort_order"]>$b["sort_order"]?1:($b["sort_order"]<$a["sort_order"]?-1:0));'));
return $result;
}
var_export(getFromParent($ary,0));
EDIT Sorry, fixed some syntax errors. Tested, and works (at least to result in what I was intending)
EDITv2 Here's the raw output from the above:
array (
0 =>
array (
'parent_category_id' => 0,
'sort_order' => 1,
'name' => 'my-category-name1',
),
1 =>
array (
'parent_category_id' => 0,
'sort_order' => 2,
'name' => 'my-category-name2',
),
)
(Used var_export just for you #FelixKling)
EDITv3 I've updated my answer to go along with the OP's update. I also now make it retain the original "category_id" values in the result array.
First you create an empty array, it will be used to store your result.
$result = array();
You need to iterate through your initial array, you can use foreach().
Then, given your parent_category_id simply use an if statement to check whether it's the given id or not.
If it is, just construct and push your result to your $result array.
Use any of the sort functions you like
Use the magic return $result;
You're done.
function returnSortedParents($categories, $target_parent){
$new_list = array();
foreach($categories as $index => $array){
//FIND ONLY THE ELEMENTS MATCHING THE TARGET PARENT ID
if($array['parent_category_id']==$target_parent){
$new_list[$index = $array['sort_order'];
}
return asort($new_list); //SORT BASED ON THE VALUES, WHICH IS THE SORTING ORDER
}

Categories