How delete array elements from string - php

I need to delete a key in an array from a string.
String is translations.fr
Array is
[
...,
translations => [
fr => [
...
],
es => [
...
]
],
...,
]
Result must be :
[
...,
translations => [
es => [
...
]
],
...,
]
I think, using exlpode and unset is the good way.
Can you help me ? Thank's

Try this
unset(ArrayName[key][key].....)

Try this :
If you want to replace the same array and delete the "fr" completely
$translationArray = unset($translationArray['fr']);
If you want to retain the previous array and save the changes in a new one
$translationArrayNew = unset($translationArray['fr']);

I think this is what you're looking for:
$str = 'translations.fr';
$exploded = explode('.', $str);
$array = [
'translations' => [
'fr' => 'fr value',
'es' => 'es value',
]
];
unset($array[$exploded[0]][$exploded[1]]);
With explode you put your string into an array containing 2 keys:
0 => translations
1 => fr
This accesses the 'translations' key within your array
$array[$exploded[0]]
and this accesses the 'fr' key within 'translations'
$array[$exploded[0]][$exploded[1]]
it's like writing: $array['translations]['fr']

Solution :
public function deleteKeyV3($keyToDelete) {
$keys = explode('.', $keyToDelete);
$result = &$array;
foreach ($keys as $key) {
if (isset($result[$key])) {
if ($key == end($keys)) {
unset($result[$key]);
} else {
$result = &$result[$key];
}
}
}
}

Related

PHP array diff: merge arrays recursively and show 'new' vs 'old' values in result

I would like to merge two arrays to compare old vs new values. For example, $arr1 is old values $arr2 is new values.
In case when the data is deleted $arr2 is an empty array. Example:
$arr1 = [
"databases" => [
0 => [
"id" => 1
"name" => "DB1"
"slug" => "db1"
"url" => "https://www.db1.org"
]
]
];
$arr2 = [];
For this my expected output after merge is
$merged = [
"databases" => [
0 => [
"id" => [
'old' => 1,
'new' => null
],
"name" => [
'old' => "DB1",
'new' => null
],
"slug" => [
'old' => "db1",
'new' => null
],
"url" => [
'old' => "https://www.db1.org",
'new' => null
],
]
]
];
if arr2 is different then the values should be present in the new field instead of null.
For example:
$arr1 = [
"databases" => [
0 => [
"id" => 1
"name" => "DB1"
"slug" => "db1"
"url" => "https://www.db1.org"
]
]
];
$arr2 = [
"databases" => [
0 => [
"id" => 5
"name" => "DB2"
"slug" => "db2"
"url" => "https://www.db2.com"
]
]
];
expected output:
$merged = [
"databases" => [
0 => [
"id" => [
'old' => 1,
'new' => 5
],
"name" => [
'old' => "DB1",
'new' => "DB2"
],
"slug" => [
'old' => "db1",
'new' => "db2"
],
"url" => [
'old' => "https://www.db1.org",
'new' => "https://www.db2.com"
],
]
]
];
Case 3 is when $arr1 is empty but $arr2 is populated:
$arr1 = [];
$arr2 = [
"databases" => [
0 => [
"id" => 1
"name" => "DB1"
"slug" => "db1"
"url" => "https://www.db1.org"
]
]
];
and the expected output is:
$merged = [
"databases" => [
0 => [
"id" => [
'old' => null,
'new' => 1
],
"name" => [
'old' => null,
'new' => "DB1"
],
"slug" => [
'old' => null,
'new' => "db1"
],
"url" => [
'old' => null,
'new' => "https://www.db1.org"
],
]
]
];
The inbuilt php functions cannot format the data in old vs new format so was wondering how to go about this? Any solutions/suggestions would be appreciated.
Update
Here is what I had tried before:
I had tried simple array_merge_recursive but it does not store the source array. So if you have $arr1 key not there, the final merged array will only have one value.
I tried some more recursive functions late in the night but failed so in essence didn't have anything to show for what I had tried. However, this morning, I came up with the solution and have posted it as an answer in case anyone needs to use it.
Interesting question, as long as a (non-empty) array on one side means to traverse into it and any skalar or null is a terminating node (while if any of old or new being an array would enforce traversing deeper so dropping the other non-array value):
It works by mapping both old and new on one array recursively and when the decision is to make to traverse to offer null values in case a keyed member is not available while iterating over the super set of the keys of both while null would represent no keys:
$keys = array_unique(array_merge(array_keys($old ?? []), array_keys($new ?? [])));
$merged = [];
foreach ($keys as $key) {
$merged['old'] = $old[$key] ?? null;
$merged['new'] = $new[$key] ?? null;
}
This then can be applied recursively, for which I found it is easier to handle both $old and $new as ['old' => $old, 'new' => $new] for that as then the same structure can be recursively merged:
function old_and_new(array $old = null, array $new = null): array
{
$pair = get_defined_vars();
$map =
static fn(callable $map, array $arrays): array => in_array(true, array_map('is_array', $arrays), true)
&& ($parameter = array_combine($k = array_keys($arrays), $k))
&& ($keys = array_keys(array_flip(array_merge(...array_values(array_map('array_keys', array_filter($arrays, 'is_array'))))))
)
? array_map(
static fn($key) => $map($map, array_map(static fn($p) => $arrays[$p][$key] ?? null, $parameter)),
array_combine($keys, $keys)
)
: $arrays;
return $map($map, $pair);
}
print_r(old_and_new(new: $arr2));
Online demo: https://3v4l.org/4KdLs#v8.0.9
The inner technically works with more than two arrays, e.g. three. And it "moves" the array keys upwards, similar to a transpose operation. Which btw. there is a similar question (but only similar, for the interesting part in context of your question it is not answered and my answer here doesn't apply there directly):
Transposing multidimensional arrays in PHP
After reviewing my own code here is the solution I came up with. I am posting it here in case someone else needs a solution for this:
/**
* Function to merge old and new values to create one array with all entries
*
* #param array $old
* #param array $new
* #return void
*/
function recursiveMergeOldNew($old, $new) {
$merged = array();
$array_keys = array_keys($old) + array_keys($new);
if($array_keys) {
foreach($array_keys as $key) {
$oldChildArray = [];
$newChildArray = [];
if(isset($old[$key])) {
if(!is_array($old[$key])) {
$merged[$key]['old'] = $old[$key];
} else {
$oldChildArray = $old[$key];
}
} else {
$merged[$key]['old'] = null;
}
if(isset($new[$key])) {
if( !is_array($new[$key])) {
$merged[$key]['new'] = $new[$key];
} else {
$newChildArray = $new[$key];
}
} else {
$merged[$key]['new'] = null;
}
if($oldChildArray || $newChildArray) {
$merged[$key] = recursiveMergeOldNew($oldChildArray, $newChildArray);
}
}
}
return $merged;
}
Note - this solution needs testing.

Recursively rename caps in array keys to dash and lower case

My array looks some what like this (is has multiple sub arrays)
[
'reportId' => '20210623-da1ece3f'
'creationDate' => '2021-06-23 19:50:15'
'dueDate' => '2021-06-24 19:50:15'
'data' => [
'vehicleDetails' => [
'chassisNumber' => 'xxxxx-xxxxxx'
'make' => 'Honda'
'model' => 'City'
'manufactureDate' => '2000'
'body' => 'xxx-xxxxx'
'engine' => 'xxx-xxx'
'drive' => 'FF'
'transmission' => 'MT'
]
]
i'm trying to rename all keys from chassisNumber to chassis_number. This is what i've done so far
function changeArrayKeys($array)
{
if(!is_array($array)) return $array;
$tempArray = array();
foreach ($array as $key=>$value){
if(is_array($value)){
$value = changeArrayKeys($value);
}else{
$key = strtolower(trim(preg_replace('/([A-Z])/', '_$1', $key)));
}
$tempArray[$key]=$value;
}
return $tempArray;
}
print_r(changeArrayKeys($data)); die;
This code kind of works. it just doesn't replace the sub keys. like here
'data' => [
'vehicleDetails' => [ ]
]
but inside vehicleDetails[] it replace properly. Any idea what im missing here? or is there a better and more efficient way to do this instead of recursively ?
This looks pretty obvious to me:
if(is_array($value)){
$value = changeArrayKeys($value);
}else{
$key = strtolower(trim(preg_replace('/([A-Z])/', '_$1', $key)));
}
If the $value is an array, you do a recursive call, but you do not change the key of that nested array in any way. It could help to move the $key manipulation out of the else branch

How can I get a value from an array by a child array value?

I've a huge problem. Somehow I need to get the variation_id from an array based on a value of a child array:
$array = [
[
'attributes' => [
'attribute_art-der-karte' => 'Rot'
],
'variation_id' => '222'
],
[
'attributes' => [
'attribute_art-der-karte' => 'Green'
],
'variation_id' => '221'
]
];
So in my case I've two things available:
The key attribute_art-der-karte
The value Rot
I found nothing here instead of a sorting. Any idea? Thanks!
Simply loop over the array and return the variation ID when your condition is met (an attribute exists with the given key and the given value):
function findVariationId(array $array, string $attrName, string $attrValue): ?string {
foreach ($array as $entry) {
if (($entry['attributes'][$attrName] ?? null) === $attrValue) {
return $entry['variation_id'];
}
}
return null;
}
Demo
You can also use array filter for this
<?php
$array = [
[
'attributes' => [
'attribute_art-der-karte' => 'Rot'
],
'variation_id' => '222'
],
[
'attributes' => [
'attribute_art-der-karte' => 'Green'
],
'variation_id' => '221'
]
];
function findByAttribute ($arr, $value) {
$result = array_filter($arr, function($elem) use($value){
return $elem['attributes']['attribute_art-der-karte'] == $value;
});
if ($result) {
return array_shift($result)['variation_id'];
}
return '';
}
var_dump(findByAttribute($array, 'Rot')); // gives '222'
var_dump(findByAttribute($array, 'a')); // returns ''
var_dump(findByAttribute($array, 'Green')); // gives 221
An additional option using array_search() and array_column():
<?php
$array = [
['attributes' => ['attribute_art-der-karte' => 'Rot'], 'variation_id' => '222'],
['attributes' => ['attribute_art-der-karte' => 'Green'], 'variation_id' => '221']
];
$key = array_search (
'Rot',
array_column(array_column($array, 'attributes'), 'attribute_art-der-karte')
);
echo "variation_id: ". (($key === false) ? 'Not found' : $array[$key]["variation_id"]);
?>
Output:
variation_id: 222
You can access the element variation_id using foreach
// One way using loops identify the non-array element
foreach($array as $ele){
if(!is_array($ele)){
print_r($ele);
}
}
// Otherway for direct calling
print_r($array['variation_id']);

Is there a PHP function like array_column that only returns single values?

I am looping through instances of associative arrays (these associative arrays themselves are part of an array).
For each array I want to return a value, based on a key.
Currently I have:
$image_path = array_column($myarray, 'uri');
But of course array_column stores its values in a array, which, considering it's only returning 1 value, is useless to me.
Is there an existing function that will allow me to just get a value based off a supplied key?
Eg:
$image_path = get_keys_value($myarray, 'uri');
Example array. This is a very basic example. The real thing has many levels to it:
$myarray = array
(
'instance' => array(
'type' => 'somedata',
'content' => somedata',
'image' => array(
'name' => 'photo',
'uri' => 'path/to/file.png'
),
),
);
Desired outcome:
$image_path contains 'path/to/file.png' string.
Try this,
function array_column_recursive(array $haystack, $needle)
{
$found = [];
array_walk_recursive($haystack, function ($value, $key) use (&$found, $needle) {
if ($key == $needle) {
$found[] = $value;
}
});
return $found;
}
echo array_column_recursive($myarray, 'uri')[0];
Here is working code.
array_column will work with only 2 level array structure.
Above array will solve your problem.
I hope this will help
I guess you can use array_map.
Ex:
$arr = [
[
'root' => [
'child1' => [
'child2' => 123
]
]
],
[
'root' => [
'child1' => [
'child2' => 456
]
]
],
[
'root' => [
'child1' => [
'child2' => 789
]
]
],
[
'root' => [
'child1' => [
'child2' => 123
]
]
],
];
print_r(array_map(function($row) {
// here goes expression to get required path
return $row['root']['child1']['child2'];
}, $arr));
Output:
Array
(
[0] => 123
[1] => 456
[2] => 789
[3] => 123
)

Recursively sort keys of a multidimensional array

I am having a hard time trying recursively sort a multidimensional array on its keys. I tried with usort(), but with no success.
Sample data:
[
'first_level' => [
'dir_3' => [
'subdir_1' => [
'file_2.mp4' => (object) [
'name' => 'file_2.mp4',
],
'file_1.mp4' => (object) [
'name' => 'file_1.mp4',
],
],
],
'dir_1' => [
'subdir_2' => [
'file_6.mp4' => (object) [
'name' => 'file_6.mp4',
],
'file_9.mp4' => (object) [
'name' => 'file_9.mp4',
],
'file_7.mp4' => (object) [
'name' => 'file_7.mp4',
],
],
'subdir_1' => [
'file_8.mp4' => (object) [
'name' => 'file_8.mp4',
],
],
],
],
]
Desired result:
[
'first_level' => [
'dir_1' => [
'subdir_1' => [
'file_8.mp4' => (object) [
'name' => 'file_8.mp4',
],
],
'subdir_2' => [
'file_6.mp4' => (object) [
'name' => 'file_6.mp4',
],
'file_7.mp4' => (object) [
'name' => 'file_7.mp4',
],
'file_9.mp4' => (object) [
'name' => 'file_9.mp4',
],
],
],
'dir_3' => [
'subdir_1' => [
'file_1.mp4' => (object) [
'name' => 'file_1.mp4',
],
'file_2.mp4' => (object) [
'name' => 'file_2.mp4',
],
],
],
],
]
Use a recursive function to call ksort on the current level and all deeper subarrays.
function recur_ksort(&$array) {
foreach ($array as &$value) {
if (is_array($value))
recur_ksort($value);
}
ksort($array);
}
recur_ksort($array);
var_export($array);
Demo: https://3v4l.org/Xede5
You need to use ksort with recursion. Demo
function recursive_ksort(&$array) {
foreach ($array as &$v) {
if (is_array($v)) {
recursive_ksort($v);
}
}
ksort($array);
}
recursive_ksort($array);
var_export($array);
function ksort_recursive(&$array)
{
if (is_array($array)) {
ksort($array);
array_walk($array, 'ksort_recursive');
}
}
It is not necessary within the recursive function to return ksort() -- this would return the unwanted success boolean value from ksort() anyhow.
Note that this function does not throw "Warning: ksort() expects parameter 1 to be array" when given a non-array - this matches my requirements but perhaps not yours. Demo: https://3v4l.org/bogAU
It is fair to assume that you'd like your data ti be sorted "naturally" -- meaning that the number portion of the directory and file names should be sorted numerically instead of as simple strings. Without sorting naturally, dir_10 will be moved in front of dir_2 because when comparing the 5th character of the two strings, 1 is less than 2.
Code: (Demo)
function nat_ksort_r(&$data): void
{
if (is_array($data)) {
ksort($data, SORT_NATURAL);
array_walk($data, __METHOD__);
}
}
nat_ksort_r($array);
var_export($array);
For natural sorting, apply the SORT_NATURAL flag to the ksort() call.
For simpler maintenance of the recursive function, recall the function using the __METHOD__ magic constant. This makes one less place to change nat_ksort_r() if you wish to call the custom function something else.
This recursive function does not return any data; it modifies the original array by reference.
The lowest level contains objects and this data does not get sorted by the function.
The above function can also use a classic loop instead of a functional iterator. (Demo)
function nat_ksort_r(&$data): void
{
if (is_array($data)) {
ksort($data, SORT_NATURAL);
foreach ($data as &$item) {
(__METHOD__)($item);
}
}
}
nat_ksort_r($array);
var_export($array);
You can even write the code in a completely anonymous fashion. Demo
$nat_ksort_r = function(&$data) use (&$nat_ksort_r) {
if (is_array($data)) {
ksort($data, SORT_NATURAL);
foreach ($data as &$item) {
$nat_ksort_r($item);
}
}
};
$nat_ksort_r($array);
var_export($array);

Categories