I have an array like this:
$arr = ({
"ID":"10",
"date":"04\/22\/20"
},
{
"ID":"20",
"date":"05\/25\/20"
},
{
"ID":"32",
"date":"07\/13\/20"
});
I want to know if values on 2 different keys exist in the array, how can I Achieve that?
Example: if id is equal to 32 and date equals to 07/13/20, return true.
I've tried in_array($monthName, array_column($GLOBALS['group_posts_array'], 'month')); but this only works on one key. I want to achieve to keys at once, kind of like && in if statement.
I don't think $arr in the question is a valid php array, but if it should be a multidimensional array, you might also pass for example an array to in_array with the keys and values that you are looking for:
$arr = [
[
"ID" => "10",
"date" => "04\/22\/20"
],
[
"ID" => "20",
"date" => "05\/25\/20"
],
[
"ID" => "32",
"date" => "07\/13\/20"
]
];
$values = [
"ID" => "32",
"date" => "07\/13\/20"
];
var_dump(in_array($values, $arr, true));
$values["ID"] = "33";
var_dump(in_array($values, $arr, true));
Output
bool(true)
bool(false)
You can implement a 'some' function.
function some(array $arr, callable $fn):bool{
foreach($arr as $index=>$item){
if($fn($item, $index)){
return true;
}
}
return false;
}
The usage would be something like the following:
$id = 32;
$date = "07/13/20";
$isInArray = some($arr, function($item, $index) use ($id, $date){
return $item->id == $id && $item->date == $date;
})
Related
I have a multidimensional array that can have any depth. What im trying to do is to filter the whole path based on dynamic keys and create a new array of it.
Example of the array
$originalArray = [
"title" => "BACKPACK MULTICOLOUR",
"description" => "description here",
"images" => [
[
"id" => 12323123123,
"width" => 635,
"height" => 560,
"src" => "https://example.com",
"variant_ids": [32694976315473, 32863017926737],
],
[
"id" => 4365656656565,
"width" => 635,
"height" => 560,
"src" => "https://example.com",
"variant_ids": [32694976315473, 32863017926737],
]
],
"price" => [
"normal" => 11.00,
"discount" => [
"gold_members" => 9.00,
"silver_members" => 10.00,
"bronze_members" => null
]
]
];
Example how the output should look like with the key "title, width, height, gold_members" filtered out. Only keys from the end of the array tree should be valid, so nothing must happen when images is in the filter
$newArray = [
"title" => "BACKPACK MULTICOLOUR",
"images" => [
[
"width" => 635,
"height" => 560,
],
[
"width" => 635,
"height" => 560,
]
],
"price" => [
"discount" => [
"gold_members" => 9.00,
]
]
];
I guess that i should create a function that loop through each element and when it is an associative array, it should call itself again
Because the filtered paths are unknown i cannot make a hardcoded setter like this:
$newArray["images"][0]["width"] = 635
The following filter will be an example but it should basically be dynamic
example what i have now:
$newArray = handleArray($originalArray);
handleArray($array)
{
$filter = ["title", "width", "height", "gold_members"];
foreach ($array as $key => $value) {
if (is_array($value)) {
$this->handleArray($value);
} else {
if (in_array($key, $filter)) {
// put this full path in the new array
}
}
}
}
[Solved] Update:
I solved my problem thanks to #trincot
I used his code and added an extra check to add an array with multiple values to the new array
My code to solve the issue:
<?php
function isListOfValues($array) {
$listOfArrays = [];
foreach ($array as $key => $value) {
$listOfArrays[] = ! is_array($value) && is_int($key);
}
return array_sum($listOfArrays) === count($listOfArrays);
}
function filterKeysRecursive(&$arr, &$keep) {
$result = [];
foreach ($arr as $key => $value) {
if (is_array($value) && ! isListOfValues($value)) {
$value = filterKeysRecursive($value, $keep);
if (count($value)) {
$result[$key] = $value;
}
} else if (array_key_exists($key, $keep)) {
$result[$key] = $value;
}
}
return $result;
}
$keep = array_flip(["title", "width", "height", "gold_members"]);
$result = filterKeysRecursive($originalArray, $keep);
You could use a recursive function, with following logic:
base case: the value associated with a key is not an array (it is a "leaf"). In that case the new object will have that key/value only when the key is in the list of desired keys.
recursive case: the value associated with a key is an array. Apply recursion to that value. Only add the key when the returned result is not an empty array. In that case associate the filtered value to the key in the result object.
To speed up the look up in the list of keys, it is better to flip that list into an associative array.
Here is the implementation:
function filter_keys_recursive(&$arr, &$keep) {
foreach ($arr as $key => $value) {
if (is_array($value)) {
$value = filter_keys_recursive($value, $keep);
if (count($value)) $result[$key] = $value;
} else if (array_key_exists($key, $keep)) {
$result[$key] = $value;
}
}
return $result;
}
$originalArray = ["title" => "BACKPACK MULTICOLOUR","description" => "description here","images" => [["id" => 12323123123,"width" => 635,"height" => 560,"src" => "https://example.com"],["id" => 4365656656565,"width" => 635,"height" => 560,"src" => "https://example.com"]],"price" => ["normal" => 11.00,"discount" => ["gold_members" => 9.00,"silver_members" => 10.00,"bronze_members" => null]]];
$keep = array_flip(["title", "width", "height", "gold_members"]);
$result = filter_keys_recursive($originalArray, $keep);
My proposition to you is to write a custom function to transform structure from one schema to another:
function transform(array $originalArray): array {
array_walk($originalArray['images'], function (&$a, $k) {
unset($a['id']); unset($a['src']);
});
unset($originalArray['description']);
unset($originalArray['price']['normal']);
unset($originalArray['price']['discount']['silver_members']);
unset($originalArray['price']['discount']['bronze_members']);
return $originalArray;
}
var_dump(transform($originalArray));
If you are familiar with OOP I suggest you to look at how DTO works in API Platform for example and inject this idea into your code by creating custom DataTransformers where you specify which kind of structers you want to support with transformer and a method where you transform one structure to another.
Iterate over the array recursively on each key and subarray.
If the current key in the foreach is a required key in the result then:
If the value is not an array, simply assign the value
If the value is an array, iterate further down over value recursively just in case if there is any other filtering of the subarray keys that needs to be done.
If the current key in the foreach is NOT a required key in the result then:
Iterate over value recursively if it's an array in itself. This is required because there could be one of the filter keys deep down which we would need. Get the result and only include it in the current subresult if it's result is not an empty array. Else, we can skip it safely as there are no required keys down that line.
Snippet:
<?php
function filterKeys($array, $filter_keys) {
$sub_result = [];
foreach ($array as $key => $value) {
if(in_array($key, $filter_keys)){// if $key itself is present in $filter_keys
if(!is_array($value)) $sub_result[$key] = $value;
else{
$temp = filterKeys($value, $filter_keys);
$sub_result[$key] = count($temp) > 0 ? $temp : $value;
}
}else if(is_array($value)){// if $key is not present in $filter_keys - iterate over the remaining subarray for that key
$temp = filterKeys($value, $filter_keys);
if(count($temp) > 0) $sub_result[$key] = $temp;
}
}
return $sub_result;
}
$result = filterKeys($originalArray, ["title", "width", "height", "gold_members"]);
print_r($result);
Online Demo
Try this way.
$expectedKeys = ['title','images','width','height','price','gold_members'];
function removeUnexpectedKeys ($originalArray,$expectedKeys)
{
foreach ($originalArray as $key=>$value) {
if(is_array($value)) {
$originalArray[$key] = removeUnexpectedKeys($value,$expectedKeys);
if(!is_array($originalArray[$key]) or count($originalArray[$key]) == 0) {
unset($originalArray[$key]);
}
} else {
if (!in_array($key,$expectedKeys)){
unset($originalArray[$key]);
}
}
}
return $originalArray;
}
$newArray = removeUnexpectedKeys ($originalArray,$expectedKeys);
print_r($newArray);
check this on editor,
https://www.online-ide.com/vFN69waXMf
I have given the array:
array(
"firstName": null,
"lastName": null,
"category": [
"name": null,
"service": [
"foo" => [
"bar" => null
]
]
]
)
that needs to be transform into this:
array(
0 => "firstName",
1 => "lastName",
2 => "category",
"category" => [
0 => "name",
1 => "service",
"service" => [
0 => "foo",
"foo" => [
0 => "bar"
]
]
]
)
The loop should check if a value is an array and if so, it should add the key as a value (0 => category) to the root of array and then leave the key as it is (category => ...) and traverse the value again to build the tree as in example.
I am stuck with this and every time I try, I get wrong results. Is there someone who is array guru and knows how to simply do it?
The code so far:
private $array = [];
private function prepareFields(array $fields):array
{
foreach($fields as $key => $value)
{
if(is_array($value))
{
$this->array[] = $key;
$this->array[$key] = $this->prepareFields($value);
}
else
{
$this->array[] = $key;
}
}
return $this->array;
}
You could make use of array_reduce:
function prepareFields(array $array): array
{
return array_reduce(array_keys($array), function ($result, $key) use ($array) {
$result[] = $key;
if (is_array($array[$key])) {
$result[$key] = prepareFields($array[$key]);
}
return $result;
});
}
Demo: https://3v4l.org/3BfKD
You can do it with this, check the Demo
function array_format(&$array){
$temp_array = [];
foreach($array as $k=>$v){
$temp_array[] = $k;
if(is_array($v)){
array_format($v);
$temp_array[$k] = $v;
}
}
$array = $temp_array;
}
array_format($array);
print_r($array);
I want to combine two different multi-dimensional arrays, with one providing the correct structure (keys) and the other one the data to fill it (values).
Notice that I can't control how the arrays are formed, the structure might vary in different situations.
$structure = [
"a",
"b" => [
"b1",
"b2" => [
"b21",
"b22"
]
]
];
$data = [A, B1, B21, B22];
Expected result:
$array = [
"a" => "A",
"b" => [
"b1" => "B1",
"b2" => [
"b21" => "B21",
"b22" => "B22"
]
]
];
You can use the following code, however it will only work if number of elements in $data is same or more than $structure.
$filled = 0;
array_walk_recursive ($structure, function (&$val) use (&$filled, $data) {
$val = array( $val => $data[ $filled ] );
$filled++;
});
print_r( $structure );
Here is a working demo
You can try by a recursive way. Write a recursive method which takes an array as first argument to alter and the data set as its second argument. This method itself call when any array element is another array, else it alters the key and value with the help of data set.
$structure = [
"a",
"b" => [
"b1",
"b2" => [
"b21",
"b22"
]
]
];
$data = ['A', 'B1', 'B21', 'B22'];
function alterKey(&$arr, $data) {
foreach ($arr as $key => $val) {
if (!is_array($val)) {
$data_key = array_search(strtoupper($val), $data);
$arr[$val] = $data[$data_key];
unset($arr[$key]);
} else {
$arr[$key] = alterKey($val, $data);
}
}
ksort($arr);
return $arr;
}
alterKey($structure, $data);
echo '<pre>', print_r($structure);
Working demo.
This should work.
$structure = [
"a",
"b" => [
"b1",
"b2" => [
"b21",
"b22"
]
]
];
$new_structure = array();
foreach($structure as $key =>$value)
{
if(!is_array($value))
{
$new_structure[$value]= $value;
}else{
foreach($value as $k =>$v)
{
if(!is_array($v))
{
$new_structure[$key][$v]=$v;
}else
{
foreach($v as $kk => $vv)
{
$new_structure[$k][$vv]=$vv;
}
}
}
}
}
print_r($new_structure);exit;
Use
$array=array_merge($structure,$data);
for more information follow this link
how to join two multidimensional arrays in php
$firstArray= [
[
"ID" => "ABC"
],
[
"ID" => "100"
],
[
"ID" => "200"
]
];
$firstArray= ["ABC" =>"fail"];
**Here I have to check two condition**
Condition #1
$abc i am having 3 values, out of this values suppose present in $second array,we have ignore the value and remaining only one value we have to assign $existarray
As of now i have completed it is working fine also,
Condition #2
I have one more json like this
$jsonString = '{
"jsonData" : {
"ABC" : {
"Count" :1
},
"100" : {
"Count" :3
},
"200" : {
"Count" :1
}
}
}';
$finalcount= json_decode($jsonString);
Now i want to check one more condition $abc array of mainarray key values count <10 we should ignore.This condition i have to implement my current code
Expected output
Array
(
[ID] => 200
)
Merging 2 condition in if statement is done with &&.
You can just mix those 2 condition in 1 for-loop:
$jsonString = '{"jsonData" : {
"ABC" : {"Count" :1},
"100" : {"Count" :3},
"200" : {"Count" :1}
}
}';
$firstArray = json_decode($jsonString, true);
$hobbies= [["ID" => "ABC"],["ID" => "100"],["ID" => "200"]];
$books= ["ABC" => true];
foreach ($Response as $key => $value) {
$id = $value['ID'];
if (!isset($books[$id]) && $firstArray["jsonData"][$id]["Count"] < 3 ) {
$selectedItem = $value;
break;
}
}
I need to sort the notification array by the value 'start_date' DESC order from the getNotifications function:
$posts_id_arr = getPoststIds($conn);
foreach ($posts_id_arr as $key => $val) {
$total_arr[$key] = [
'notification' => getNotifications($val['post_id'], $user_id, $conn)
];
}
$response_array = array('data' => $total_arr, 'more things' => $more_array);
echo json_encode($response_array);
right now the order is by post id due to the foreach loop.
data {
notification:
[
{
post_id: “1",
start_date: "2016-10-10 08:00:00",
},
{
post_id: “1",
start_date: "2016-10-10 12:00:00",
}
],
notification:
[
post_id: “2",
start_date: "2016-10-10 09:00:00",
},
{
post_id: “2",
start_date: "2016-10-10 13:00:00",
}
]
}
And i need it to be 1: 08:00, 2: 09:00, 1: 12:00, 2: 13:00
You can use a custom function to sort the values in the array using uasort. Your date format is sortable using strcmp - a date in the past is lower than a date in the future, so you can use this in your comparator.
function sort_by_date($a, $b) {
return strcmp($a->start_date, $b->start_date);
}
$sorted_posts = uasort($total_arr->notification, 'sort_by_date');
$response_array = array('data' => $sorted_posts, 'more things' => $more_array);
However, you don't need to perform the sort inside the foreach.
You can try the below code. Change the variable name accordingly.
foreach ($points as $key => $val) {
$time[$key] = $val[0];
}
array_multisort($time, SORT_ASC, $points);
This is because of the way array_multisort works. It sorts multiple arrays, and when the $time array is sorted, the $points array is re-ordered according to the array indices in $time. The array_multisort should come after the foreach, though
Hope this would be helpful.
If you want to sort with the inner array you can better prefer for the usort() method.
usort — Sort an array by values using a user-defined comparison function
This function will sort an array by its values using a user-supplied comparison function. If the array you wish to sort needs to be sorted by some non-trivial criteria, you should use this function.
<?php
function cmp($a, $b)
{
return strcmp($a["fruit"], $b["fruit"]);
}
$fruits[0]["fruit"] = "lemons";
$fruits[1]["fruit"] = "apples";
$fruits[2]["fruit"] = "grapes";
usort($fruits, "cmp");
while (list($key, $value) = each($fruits)) {
echo "\$fruits[$key]: " . $value["fruit"] . "\n";
}
?>
When sorting a multi-dimensional array, $a and $b contain references to the first index of the array.
The above example will output:
$fruits[0]: apples
$fruits[1]: grapes
$fruits[2]: lemons
Alternative Solution:
You can try array_multisort() since it will sort the array based on the order that you need.
$arr = your array;
$sort = array();
foreach($arr as $k=>$v) {
$sort['field'][$k] = $v['field'];
}
array_multisort($sort['field'], SORT_DESC, $arr);
echo "<pre>";
print_r($arr);
You can follow these steps:
1) Define a temporary array to store the dates:
foreach ($total_arr['notification'] as $vals) {
$temp_dates[] = $vals['start_date'];
}
2) Sort the newly created temp array using arsort():
arsort($temp_dates);
3) Use array_filter() and loop to find corresponding post ids and store to resulting array:
$i = 0;
foreach ($total_arr['notification'] as $vals) {
$res['notification'][] = array_filter($vals, function($val) {
return $val['start_date'] == $temp_dates[$i];
});
$i++;
}
$response_array = array('data' => $res, 'more things' => $more_array);
Note: Not sure if it works with duplicate start_dates.
As iblamefish mentioned, using uasort() is the way to go - it is simple an both memory and processing efficient, compared to all the other answers. But while strcmp() does produce good results for the SQL-style dates that you have, it is not the "correct" way to handle time fields - you should parse the times and compare them as time values:
$a = [
[ "name" => "foo", "date" => "2016-08-09 10:30:00" ],
[ "name" => "bar", "date" => "2016-01-09 02:00:00" ],
[ "name" => "baz", "date" => "2016-11-02 18:21:34" ]
];
uasort($a, function($a,$b) {
return (new DateTime($a->date))->getTimestamp() - (new DateTime($b->date))->getTimestamp();
});
var_dump($a);
produces:
array(3) {
[0] =>
array(2) {
'name' =>
string(3) "foo"
'date' =>
string(19) "2016-08-09 10:30:00"
}
[1] =>
array(2) {
'name' =>
string(3) "bar"
'date' =>
string(19) "2016-01-09 02:00:00"
}
[2] =>
array(2) {
'name' =>
string(3) "baz"
'date' =>
string(19) "2016-11-02 18:21:34"
}
}
Also, closures (anonymous functions) are more fun than using old-style "text callables").