I have a large array converted from a JSON structure with an unknown number of elements and sublevels.
Something like:
$marr = array
(
"Order" => array
(
"Details" => array
(
"Document" => array
(
"Number" => "1585636772",
"Date" => "2014-12-31"
),
"Delivery" => array
(
"Date" => "2015-01-02",
"TrackingCode" => "5703",
"Name" => "Example Name",
"Address" => "Example Address"
)
)
)
);
And on the other hand, I have an array of items I need to compare and find out if they are in the array above. This "indexer" will always mirror the same structure above (it's generated before the comparison step) because I thought that would help me ensure a proper comparison in an easier way.
Something like:
$indexer = array
(
"Order" => array
(
"Details" => array
(
"Document" => array
(
"Date" => "variable_name_1"
),
"Delivery" => array
(
"Date" => "variable_name_2"
)
)
)
);
I'm not entirely sure how best to compare these. I have looked into array_walk_recursive() which only returns the leaf values and I have tried to write my own attempts at a basic recursive function that would perform a foreach() which would then try to do something like:
if( isset($marr["Order"]["Details"]["Document"]["Date"]) )
{
$store[ $indexer["Order"]["Details"]["Document"]["Date"] ] = $marr["Order"]["Details"]["Document"]["Date"];
}
So that at the end I would have a basic array that stored all values found on $marr under an alias that was listed on $indexer. Like this:
$store["variable_name_1"] = "2014-12-31";
$store["variable_name_2"] = "2015-01-02";
This has been a headache for two days now and I can't seem to figure out the best way to go through this. I'm trying to walk through $indexer to reach its ending, obtain the "variable name", and then compare with $marr to store its data, but I always seem to lose the parent nodes of $indexer while trying to do this recursively. I would appreciate any advice at all.
You could use this recursive function:
function storeFromIndex($marr, $indexer) {
if (!is_array($indexer)) {
return array($indexer => $marr);
}
$store = [];
foreach($indexer as $key => $subindexer) {
$store = array_merge($store, storeFromIndex($marr[$key], $subindexer));
}
return $store;
}
And then call it like this:
$store = storeFromIndex($marr, $indexer);
With the example data given, $store will be:
array (
'variable_name_1' => '2014-12-31',
'variable_name_2' => '2015-01-02',
)
Here I would like to suggest do not maintain indexer, you can use iterator and create new array using associated keys.
For example have a look on below solution:
$array = array
(
"Order" => array
(
"Details" => array
(
"Document" => array
(
"Number" => "1585636772",
"Date" => "2014-12-31"
),
"Delivery" => array
(
"Date" => "2015-01-02",
"TrackingCode" => "5703",
"Name" => "Example Name",
"Address" => "Example Address"
)
)
)
);
$new_array = array();
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach ($iterator as $key => $value) {
$keys = array();
$keys[] = $key;
for ($i = $iterator->getDepth() - 1; $i >= 0; $i--) {
$keys[] = $iterator->getSubIterator($i)->key();
}
$key_paths = array_reverse($keys);
$new_array[implode('_', $key_paths)] = $value;
}
print_r($array);
print_r($new_array);
Output:
Array
(
[Order] => Array
(
[Details] => Array
(
[Document] => Array
(
[Number] => 1585636772
[Date] => 2014-12-31
)
[Delivery] => Array
(
[Date] => 2015-01-02
[TrackingCode] => 5703
[Name] => Example Name
[Address] => Example Address
)
)
)
)
Array
(
[Order_Details_Document_Number] => 1585636772
[Order_Details_Document_Date] => 2014-12-31
[Order_Details_Delivery_Date] => 2015-01-02
[Order_Details_Delivery_TrackingCode] => 5703
[Order_Details_Delivery_Name] => Example Name
[Order_Details_Delivery_Address] => Example Address
)
Related
I've an array in PHP that can have multiple same labels, but creation date for each one is different. I try to filter my array and keep only old created data when duplicates labels exists in my array.
The array look like this :
$data = [
[0] => Array
(
[label] => "Same label"
[created] => "15/01/2022" //recent data
)
[1] => Array
(
[label] => "Same label"
[created] => "11/01/2022" //oldest data to unset
)
[2] => Array
(
[label] => "Label alone"
[created] => "18/01/2022"
)
]
And have to be like this :
$data = [
[0] => Array
(
[label] => "Same label"
[created] => "11/01/2022"
)
[1] => Array
(
[label] => "Label alone"
[created] => "18/01/2022"
)
]
Actually, I've tried with an array_search() but process is long if they are a lot of data. I can remove duplicate data but I don't know how to filter by created date only on duplicate labels in my array...
$data = [];
foreach ($prs as $pr) {
$exist = array_search($pr->getLabel, array_column($data, 'label'), TRUE);
if (!$exist){
$data[] = [
'label' => $pr->getLabel(),
'created' => $pr->getCreationDate(),
];
}
}
I saw that there are also array_filter() function, array_key_exists() and other good functions.
Can you help me please ?
I'll add more info as I progress, but I can't show all page or processes (just example because they are confidential data).
Thank you a lot for your help.
One way might be doing a dual sort by label and by date. Then after sorting, you can loop the collection and compare the current and the next label.
If the next label is different from the current or the last one in the collection, then add the item to the $result array.
The example uses a spaceship operator <=> for the comparison in the usort function and is available since php 7
$data = [
["label" => "test1","created" => "31/12/2022"],
["label" => "Same label", "created" => "13/01/2022"],
["label" => "Same label", "created" => "15/01/2022"],
["label" => "Same label", "created" => "11/01/2022"],
["label" => "Label alone","created" => "18/01/2022"],
["label" => "test1","created" => "30/12/2022"],
];
usort($data, function($a, $b) {
if ($a["label"] === $b["label"]) {
return DateTime::createFromFormat("d/m/Y", $a["created"]) <=> DateTime::createFromFormat("d/m/Y", $b["created"]);
}
return $a["label"] <=> $b["label"];
});
$result = [];
$last = count($data) - 1;
for ($i = 0; $i < count($data); $i++) {
if ((isset($data[$i + 1]) && $data[$i + 1]["label"] !== $data[$i]["label"]) || $i === $last) {
$result[] = $data[$i];
}
}
print_r($result);
Output
Array
(
[0] => Array
(
[label] => Label alone
[created] => 18/01/2022
)
[1] => Array
(
[label] => Same label
[created] => 15/01/2022
)
[2] => Array
(
[label] => test1
[created] => 31/12/2022
)
)
See a PHP demo.
I need to remove objects from a 3d array where the same two-property object is found in any other row.
I previously asked this similar question, but my new requirements are slightly different because I was keeping one of the encountered duplicates. Now I would like for both/all encountered duplicates to be removed.
[
[
["name" => "John", "surname" => "Smith"],
["name" => "Kate", "surname" => "Winston"]
],
[
["name" => "Kate", "surname" => "Winston"],
["name" => "Jack", "surname" => "Irving"]
],
]
Desired filtered result with same original structure:
[
[
["name" => "John", "surname" => "Smith"],
],
[
["name" => "Jack", "surname" => "Irving"]
],
]
Seems like others answers don't see their own final results and don't read desired output.
Here a little bit hard solution but it works well.
Note: the input data array must have 2 object indexes and 2 arrays of objects for comparing, otherwise, it should be fixed.
$ar = Array (
0 => [(object)["name"=>"John", "surname"=>"Smith"], (object)["name"=>"Kate", "surname"=>"Winston"]],
1 => [(object)["name"=>"Kate", "surname"=>"Winston"], (object)["name"=>"Jack", "surname"=>"Irving"]]
);
$arr = [];
$k = 0; // do `if statement` only once
foreach($ar as $num=>&$subar){
foreach($subar as $num2=>$pers){
$subar[$num2] = (array)$pers; // object to array
if (!$k) {
$keys = array_keys($subar[$num2]); // collect "name" and "surname" in an array
$k++;
}
foreach($subar[$num2] as $a=>$b){
$seq = array_search($a,$keys); // index of the current key
if (!$seq) { // 0 -> "name", 1 -> "surname"
$arr[$num][$b] = '';
} else {
$arr[$num][$subar[$num2][current($keys)]] = $b;
}
}
}
}
$diff[] = array_diff($arr[0],$arr[1]); // clear duplicates from 1-st set
$diff[] = array_diff($arr[1],$arr[0]); // clear duplicates from 2-nd set
Gives result:
Array
(
[0] => Array
(
[John] => Smith
)
[1] => Array
(
[Jack] => Irving
)
)
And after you can re-create the output array:
// creating a new array
$res = [];
foreach($diff as $num=>$ns){
foreach($ns as $name=>$surname){
foreach($keys as $ind=>$key){
if ($ind % 2 == 0){
$tmp[$key] = $name; // put name
} else {
$tmp[$key] = $surname; // put surname
}
}
$res[$num] = (object)$tmp; // array to object
}
}
Output will be:
Array
(
[0] => stdClass Object
(
[name] => John
[surname] => Smith
)
[1] => stdClass Object
(
[name] => Jack
[surname] => Irving
)
)
Demo
In case of string values in the input arrays, i.e.:
$ar = [
'[{"name":"John", "surname":"Smith"}, {"name":"Kate", "surname":"Winston"}]',
'[{"name":"Kate", "surname":"Winston"}, {"name":"Jack", "surname":"Irving"}]'
];
You need a little fix:
...
foreach($ar as $num=>&$subar){
$ar[$num] = json_decode($subar);
foreach($subar as $num2=>$pers){
...
The same output you will get.
Demo
It's easier if you don't trim away the brackets [], as you stated that you did in the comments. That way, they are proper JSON strings, which we can use in PHP.
Map (or loop) over your array, and build up a $result array, where you append all the arrays from your decoded JSON. Once you have your final $result, you have an array that looks somewhat like
Array (
[0] => Array
(
[name] => John
[surname] => Smith
)
[1] => Array
(
[name] => Kate
[surname] => Winston
)
[2] => Array
(
[name] => Kate
[surname] => Winston
)
[3] => Array
(
[name] => Jack
[surname] => Irving
)
)
We have all the values in an actual array now, but there are duplicates -- which can be removed using array_unique() with the SORT_REGULAR flag.
$array = [
'[{"name":"John", "surname":"Smith"}, {"name":"Kate", "surname":"Winston"}]',
'[{"name":"Kate", "surname":"Winston"}, {"name":"Jack", "surname":"Irving"}]'
];
$result = [];
array_map(function($v) use (&$result) {
$result = array_merge($result, json_decode($v, true));
}, $array);
print_r(array_unique($result, SORT_REGULAR));
Final output:
Array
(
[0] => Array
(
[name] => John
[surname] => Smith
)
[1] => Array
(
[name] => Kate
[surname] => Winston
)
[3] => Array
(
[name] => Jack
[surname] => Irving
)
)
Live demo at https://3v4l.org/q6pZc
$array = [
'[{"name":"John", "surname":"Smith"}, {"name":"Kate", "surname":"Winston"}]',
'[{"name":"Kate", "surname":"Winston"}, {"name":"Jack", "surname":"Irving"}]'
];
$resultArray = [];
foreach ($array as $item) {
$bufferArray = array_merge($resultArray, json_decode($item));
foreach ($bufferArray as $elements) {
$key = $elements->name . $elements->surname;
if (array_key_exists($key, $resultArray)) {
unset($resultArray[$key]);
} else {
$resultArray[$key] = $elements;
}
}
}
print_r($resultArray);
Output
Array
(
[KateWinston] => stdClass Object
(
[name] => Kate
[surname] => Winston
)
[JackIrving] => stdClass Object
(
[name] => Jack
[surname] => Irving
)
)
can rewrite this into more serious code )
To remove objects from each row where a given object exists any where in any other row, you can make iterates calls of array_udiff(). Inside the function, the first parameter should be the currently iterated row and the next/subsequent parameter(s) should all of the other rows in the entire array. The last parameter is the callback which compares whole objects to whole objects via PHP's performance-optimized algorithm.
My snippet below will not only handle your 2-row array, it will also handle arrays with 3 or more rows.
Code: (Demo)
$result = [];
foreach ($array as $i => $objs) {
$cache = $array[$i];
unset($array[$i]);
$params = [
$objs,
...$array,
fn($a, $b) => $a <=> $b
];
$result[] = array_udiff(...$params);
$array[$i] = $cache;
}
var_export($result);
To be clear, this snippet will work the same if the array of arrays of objects is an array of arrays of arrays.
I've received and XML, converted it into an array for usage.
The XML comes in unpredictable multi dimension when I convert it into array.
Had been looking around but could not find a suitable solution.
An alternative is to simplify the converted array.
I've converted an XML to array in PHP, and the result looked like this:
Array
(
[GetMLCBRes] => Array
(
[0] => Array
(
[Ord] => Array
(
[0] => Array
(
[OrdId] => Array
(
[0] => DP Order ID
)
)
)
[ReqInf] => Array
(
[0] => Array
(
[ReqDat] => Array
(
[0] => Date of Request
)
)
)
[Res] => Array
(
[0] => PDF Report
)
)
)
)
May I know how to drop the index like [0] but remain the assoc keys like [Ord], [OrdId], [ReqInf] and [Res], etc.
How to convert it to become like this?
Array
(
[GetMLCBRes] => Array
(
[Ord] => Array
(
[OrdId] => DP Order ID
)
[ReqInf] => Array
(
[ReqDat] => Date of Request
)
[Res] => PDF Report
)
)
it works but if you change your structure maybe it won't. It's not optimized too :)
$input = Array(
'GetMLCBRes' => Array(Array(
'Ord' => Array(Array(
'OrdId' => Array('DP Order ID')
)),
'ReqInf' => Array(Array(
'ReqDat' => Array('Date of Request')
)),
'Res' => Array('PDF Report')
))
);
foreach($input as &$in){
$sub = $in[0];
foreach($sub as $key => &$value){
$sub2 = $value[0];
if(!is_array($sub2)){
$sub[$key] = $sub2;
continue;
}
$final2 = array();
foreach($sub2 as $key2 => $final)
$final2[$key2] = $final[0];
$sub[$key] = $final2;
}
$in = $sub;
}
var_dump($input);
You can test it here : http://sandbox.onlinephpfunctions.com/code/a6770c7649d7d277aa1dc3544093cc87bed0951d
This should work as expected :
function recursive_skip(array $ary) {
foreach($ary as $k => &$v) {
if (is_array($v)) {
// Skip it
$v = $v[0];
}
if (is_array($v)) {
// If current array item is an array itself, recursively call the function on it
$v = recursive_skip($v);
}
}
// Return updated array to calling context
return $ary;
}
$source = Array(
'GetMLCBRes' => Array(Array(
'Ord' => Array(Array(
'OrdId' => Array('DP Order ID')
)),
'ReqInf' => Array(Array(
'ReqDat' => Array('Date of Request')
)),
'Res' => Array('PDF Report')
))
);
$dest = recursive_skip($source);
var_dump($dest);
A few caveats : the function will only skip one array level each time (but could be adapted to handle more if needed) and might come with a significant performance cost if your source array is huge since it's recursive (O(n)), it just walks through the whole array tree.
I have a multidimensional array.
$shop = array(
array("appn1", "pub1" ,"pub2" , "pub3"),
array("appn2", "pub1"),
array("appn3", "pub1" ,"pub2")
);
The first item in each array is application number and the rest in each array are the publication numbers. I get the first item(application number) and the last item of each array(latest publication number) like this
$index = count(array_keys($shop));
for($i=0;$i<$index;$i++){
$appln_nr = $shop[$i][0];
echo $appln_nr;
$publn_nr_index = count(array_keys($shop[$i]))-1;
$publn_nr = $shop[$i][$publn_nr_index];
echo $publn_nr;
}
Now I have application number and publication number for each inner array.
I want to create an associative array from the application numbers and publication numbers.
where the key should be the application number and its value is the publication number.
Thanks
EDIT
What I am getting from $shop array
Array
(
[0] => Array
(
[0] => appn1
[1] => pub1
[2] => pub2
[3] => pub3
)
[1] => Array
(
[0] => appn2
[1] => pub1
)
[2] => Array
(
[0] => appn3
[1] => pub1
[2] => pub2
)
)
And this is what I need in my associative array
Array(
"appn1" => "pub3"
"appn2" => "pub1"
"appn3" => "pub2"
)
Finally i understood what you wanted, after your edit XD:
$shop = array(
array("appn1", "pub1" ,"pub2" , "pub3"),
array("appn2", "pub1"),
array("appn3", "pub1" ,"pub2")
);
$shopNew = array();
foreach($shop as $value){
$shopNew[$value[0]] = end($value);
}
// now if you want you can replace $shop and unset $shopNew
$shop = $shopNew;
unset($shopNew);
print_r($shop);
the output is this:
Array (
[appn1] => pub3
[appn2] => pub1
[appn3] => pub2
)
You can try
$shop = array(
array("appn1","pub1","pub2","pub3"),
array("appn2","pub1"),
array("appn3","pub1","pub2")
);
$final = array();
array_map(function ($var) use(&$final) {$final[reset($var)] = end($var);}, $shop);
var_dump($final);
Output
array
'appn1' => string 'pub3' (length=4)
'appn2' => string 'pub1' (length=4)
'appn3' => string 'pub2' (length=4)
You can easily convert your array into a new format by using the first element as key (see reset) and the last element (see end) as value:
foreach($shop as $fl) {
$v[reset($fl)] = end($fl);
}
Result is in $v then.
If you want to transform the array you need to delete each element as well:
foreach($shop as $v => $fl) {
$shop[reset($fl)] = end($fl);
unset($shop[$v]);
}
Result is in $shop then. Unset takes care of removing from the array.
Output in both cases is:
array(3) {
'appn1' =>
string(4) "pub3"
'appn2' =>
string(4) "pub1"
'appn3' =>
string(4) "pub2"
}
try this:
foreach($shop as $k => $v) {
$new_arr[$v[0]] = end($v);
}
It should give you this result,
$new_arr = array(
"appn1" => "pub3",
"appn2" => "pub1",
"appn3" => "pub2"-
);
You can also create like this,
$arrField = [];
$arrField['id'] = '0001';
$arrField["first_name"] ='Demo Test';
print_r($arrField);
print_r($arrField) display output like this.
Array ( [id] => 0001 [first_name] => Demo Test )
I've got an multi-array (currently with objects) that I want to reorder based on a specific key/value.
Array
(
[0] => stdClass Object
(
[task_id] => 1
[task_title] => Title
[users_username] => John
)
[1] => stdClass Object
(
[task_id] => 2
[task_title] => Title
[users_username] => John
)
[2] => stdClass Object
(
[task_id] => 3
[task_title] => Title
[users_username] => Mike
)
)
I'd like to reorder it to get multi-arrays by user_name, so I can cycle through the task by username.
Array
(
[John] => Array
(
[0] => Array
(
[task_id] => 1
[title] => Title
)
[1] => Array
(
[task_id] => 2
[title] => Title
)
)
[Mike] => Array
(
[0] => Array
(
[task_id] => 3
[title] => Title
)
)
)
Is it possible to recreate my array to an array like that above?
Updated version of the code
<?php
$it0 = (object) array('task_id' => 1,'task_title' => 'Title','users_username' => 'John');
$it1 = (object) array('task_id' => 2,'task_title' => 'Title','users_username' => 'John');
$it2 = (object) array('task_id' => 3,'task_title' => 'Title','users_username' => 'Mike');
$array = array($it0,$it1,$it2);
$return = array();
foreach($array as $id => $value){
$return[$value->users_username][] = array('task_id' => $value->task_id,'title' => $value->task_title);
}
var_dump($return);
Yes, it is possible.
You'll have to loop through your current array and create a new array to do it.
example:
$new_array = array();
foreach ($array as $row)
{
$new_row = array(
'task_id' => $row->task_id,
'title' => $row->task_title,
);
$name = $row->users_username;
if (isset($new_array[$name]))
{
$new_array[$name][] = $new_row;
}
else
{
$new_array[$name] = array($new_row);
}
}
Now $new_array contains the new array exactly like the one you're asking for.
Then you can sort it with
ksort($new_array);
There may be another way to do this, with some built-in function, but sometimes I'd rather just do it myself, and know how it is working, without having to look up the documentation.
The approach:
Iterate through all of the first array, looking at [users_username] and putting them into a new array.
Code:
$dst_array = array();
foreach ($src_array as $val)
{
$user = $val->users_username;
// TODO: This check may be unnecessary. Have to test to find out.
// If this username doesn't already have an array in the destination...
if (!array_key_exists($user, $dst_array))
$dst_array[$user] = array(); // Create a new array for that username
// Now add a new task_id and title entry in that username's array
$dst_array[$user][] = array(
'task_id' => $val->task_id
'title' => $val->title
);
}
Just something like this (maybe not 100% PHP code):
foreach ( $obj : $oldArray ) {
$newTask['task_id'] = $obj->task_id;
$newTask['title'] = $obj->title;
$newArray[$oldName][] = $newTask;
}
If you want to order it; you can just call a order function afterwards.