I have an associative array:
$input = [
['key'=>'x', 'value'=>'a'],
['key'=>'x', 'value'=>'b'],
['key'=>'x', 'value'=>'c'],
['key'=>'y', 'value'=>'d'],
['key'=>'y', 'value'=>'e'],
['key'=>'z', 'value'=>'f'],
['key'=>'m', 'value'=>'n'],
];
And I want to reform it simple in:
$output = [
'x'=>['a','b','c'],
'y'=>['d','e'],
'z'=>'f',
'm'=>'n'
]
So basically, conditions are:
1. If same key found then put values in an array.
2. If no same key found then value remains string.
You can replace associative array with object if you are more comfortable with objects.
Here is my working solution for this problem:
foreach($input as $in){
if(!empty($output[$in['key']])){
if(is_array($output[$in['key']])){
$output[$in['key']][] = $in['value'];
continue;
}
$output[$in['key']] = [$output[$in['key']],$in['value']];
continue;
}
$output[$in['key']] = $in['value'];
}
print_r($output);
However I believe that it can be done in much compact and efficient way.
Please comment your answers if someone has better solution.
Your help is much appreciated!
Reformat array to [ [ x=>a ], [x=>b],.. ] and merge all sub-arrays
$input = array_map(function($x) { return [$x['key'] => $x['value']]; }, $input);
$input = array_merge_recursive(...$input);
print_r($input);
demo
I would suggest
<?php
$input = [
['key'=>'x', 'value'=>'a'],
['key'=>'x', 'value'=>'b'],
['key'=>'x', 'value'=>'c'],
['key'=>'y', 'value'=>'d'],
['key'=>'y', 'value'=>'e'],
['key'=>'z', 'value'=>'f'],
['key'=>'m', 'value'=>'n'],
];
$reducer = function($carry, $item) {
$carry[$item['key']][] = $item['value'];
return $carry;
};
$mapper = function ($item) {
if (count($item) === 1) {
return $item[0];
}
return $item;
};
$output = array_map($mapper, array_reduce($input, $reducer, []));
var_dump($output);
You can see the result here: https://3v4l.org/8JjjS
You can use array_reduce to loop over an existing array and build up a new one:
$output = array_reduce($input, function ($carry, $i) {
$carry[$i['key']][] = $i['value'];
return $carry;
}, []);
Each element in $input is passed to the anonymous function, along with the $carry variable that's being built up as we go along. Inside, we just add each value to a sub-element indexed by key. The third argument [] is to set the initial value of the result to an empty array.
See https://eval.in/935015
(I'm assuming that the duplicate x keys in the question are a typo, and that the second is supposed to z, since that matches up with your suggested output)
For your original code you may find extract() interesting. I replaced the continue-s with else-s, but that is more like a matter of taste:
foreach($input as $in){
extract($in);
if(!empty($output[$key])){
if(is_array($output[$key])){
$output[$key][] = $value;
} else {
$output[$key] = [$output[$key],$value];
}
} else {
$output[$key] = $value;
}
On a side note I would probably use two much simpler loops, one for building lists and another for extracting single elements:
foreach($input as $in){
$output[$in['key']][] = $in['value'];
/* or: extract($in);
$output[$key][]=$value; */
}
foreach($output as $key => $value){
if(count($value)==1){
$output[$key]=$value[0];
}
}
Related
I am brand new to php.I have found questions that show how to remove key/value pairs from JSON files with php, but not array indexes.
I have worked out how to append values to arrays in a JSON file with json_decode(). But not how to remove values. I need to produce a function() that hunts for c and removes any value within an array in my JSON file. Below is a before and after of the expected outcome I need to produce with my php file.
// before
[["a", "c", "b"], ["c", "c"], [], ["c", "d"], ["d"], ["e"]]
// after
[["a", "b"], [], [], ["d"], ["d"], ["e"]]
Below is the function I have produced in order to add values to arrays in my JSON if this helps provide more context:
function appendClient($file, $post, $client) {
$a = fopen($file, "r");
$json = json_decode(fread($a, filesize($file)));
$json[$post][] = $client;
fclose($a);
$a = fopen($file, "w");
fwrite($a, json_encode($json));
fclose($a);
}
Use array_filter
function removeClient($file, $post, $client) {
$json = json_decode(file_get_contents($file));
$json[$post] = array_filter($json[$post], function($x) use($client) {
return $x != $client;
});
file_put_contents($file, json_encode($json));
}
This assumes all the elements of the array are either empty arrays or 1-element arrays containing the client name, as in the example you showed.
Take a look at array_filter and array_values functions.
[["a"],[],["b"],["c"]]
From the above input, I am assuming you are working with 2d array. Then, you can use the following function to do the job:
function removeValues($array, $value) {
$result = [];
foreach ($array as $row) {
$filtered = array_filter($row, function($entry) use($value) {
return $entry != $value;
});
// If you need to reset keys
$filtered = array_values($filtered);
$result[] = $filtered;
}
return $result;
}
Example:
$input = [["a"],[],["b"],["c"]];
$output = removeValues($input, "c");
print_r($output);
How i can remove a value from array what is not fully match the letters.
Array code example:
$Array = array(
'Funny',
'funnY',
'Games',
);
How I can unset all values from this array what is 'funny'
I try via unset('funny'); but is not removing the values from array, is removed just if i have 'funny' on array but 'funnY' or 'Funny' not working
Maybe there is some sophisticated solution with array_intersect_key or something which could do this in one line but I assume this approach is more easily read:
function removeCaseInsensitive($array, $toRemove) {
$ret = [];
foreach ($array as $v) {
if (strtolower($v) != strtolower($toRemove))
$ret[] = $v;
}
return $ret;
}
This returns a new array that does not contain any case of $toRemove. If you want to keep the keys than you can do this:
function removeCaseInsensitive($array, $toRemove) {
$keep = [];
foreach ($array as $k => $v) {
if (strtolower($v) != strtolower($toRemove))
$keep[$k] = true;
}
return array_intersect_keys($array, $keep);
}
You can filter out those values with a loose filtering rule:
$array = array_filter($array, function($value) {
return strtolower($value) !== 'funny';
});
on http://php.net/manual/en/function.in-array.php - if you scroll down it gives a function to determine if a string is inside of a query in a multidimensional array. "If you found yourself in need of a multidimensional array in_array like function you can use the one below. Works in a fair amount of time"
Here's original code(working):
function in_multiarray($elem, $array)
{
$top = sizeof($array) - 1;
$bottom = 0;
while($bottom <= $top)
{
if($array[$bottom] == $elem)
return true;
else
if(is_array($array[$bottom]))
if(in_multiarray($elem, ($array[$bottom])))
return true;
$bottom++;
}
return false;
}
What I'm trying to do is instead of returning 'true' or 'false' - i'd like to return the ROW #. So my initial thought was to simply replace 'return true' with 'return $bottom; however it isn't returning the record number.
Modified Function (not working);
function in_multiarray($elem, $array)
{
$top = sizeof($array) - 1;
$bottom = 0;
while($bottom <= $top)
{
if($array[$bottom] == $elem)
return $bottom;
else
if(is_array($array[$bottom]))
if(in_multiarray($elem, ($array[$bottom])))
return $bottom;
$bottom++;
}
return false;
}
Does anyone have an idea how to modify this function to return the ROW number that contains the match?
Here's a sample of the array...
$sample = array
array ("oldpage1.php","newpage1.php"),
array ("oldpage2.php","newpage2.php"),
array ("oldpage3.php","newpage3.php"),
array ("oldpage4.php","newpage4.php"),
array ("oldpage5.php","newpage5.php")
etc.
);
$row = in_multiarray($input, $sample);
Therefore if we know the row # we can fetch the new page with a simple
$newpage=$sample[$row][1]
Thanks!
It's worth noting that a function like in_array is intended to tell you whether or not a value exists inside of an array. What you're looking for seems to be a lot closer to something like array_search, which is designed to actually provide you with the key that points to a given value in the array.
However, because you're using a multi-dimensional array what you're actually looking for is the key that points to the array that contains the value. Hence we can divide and conquer this problem with two simple steps.
Map
Filter
The first step is to map a function in_array to every element in the first array (which is just another array). This will tell us which elements of the primary array contain an array that contains the value we're searching for.
$result = array_map(function($arr) use($search) {
return in_array($search, $arr, true);
}, $arr, [$searchValue]);
The second step is to then return the keys to those arrays (i.e. filter the result).
$keys = array_keys(array_filter($result));
Now you have all the keys of any matching items. If you want to apply as just one custom filter that specifies exactly where in the subarray to look, you could also do it like this.
$search = "oldpage2.php";
$sample = [
["oldpage1.php","newpage1.php"],
["oldpage2.php","newpage2.php"],
["oldpage3.php","newpage3.php"],
["oldpage4.php","newpage4.php"],
["oldpage5.php","newpage5.php"],
];
$keys = array_keys(array_filter($sample, function($arr) use($search) {
return $arr[0] === $search;
}));
var_dump($keys);
And you get...
array(1) {
[0]=>
int(1)
}
So now you know that "oldpage2.php" is in row 1 in $sample[1][0] which means you can do this to get the results out of the array.
foreach($keys as $key) {
echo "{$sample[$key][0]} maps to {$sample[$key][1]}\n";
}
Giving you
oldpage2.php maps to newpage2.php
If you want to return only the first result you could do that as well with a function like this using similar approach.
function getFirstMatch($search, Array $arr) {
foreach($arr as $key => $value) {
if ($value[0] === $search) {
return $value[1];
}
}
}
echo getFirstMatch("oldpage4.php", $sample); // newpage4.php
The Better Alternative
Of course, the better approach is to actually use the oldpage names as the actual keys of the array rather than do this expensive search through the array, because array lookup by key in PHP is just an O(1) operation, whereas this needle/haystack approach is O(N).
So we turn your $samples array into something like this and the search no longer requires any functions...
$samples = [
"oldpage1.php" => "newpage1.php",
"oldpage2.php" => "newpage2.php",
"oldpage3.php" => "newpage3.php",
"oldpage4.php" => "newpage4.php",
"oldpage5.php" => "newpage5.php",
];
Now you can just do something like $newpage = $samples[$search] and you get exactly what you're looking for. So echo $samples["oldpage2.php"] gives you "newpage2.php" directly without the intermediary step of searching through each array.
You can use the following code to get the full path to the value:
function in_multiarray($elem, $array, &$result)
{
$top = sizeof($array) - 1;
$bottom = 0;
while($bottom <= $top)
{
if($array[$bottom] == $elem) {
array_unshift($result, $bottom);
return true;
}
else {
if(is_array($array[$bottom])) {
if(in_multiarray($elem, $array[$bottom], $result)) {
array_unshift($result, $bottom);
return true;
}
}
}
$bottom++;
}
array_shift($result);
return false;
}
$sample = array(
array ("oldpage1.php","newpage1.php"),
array ("oldpage2.php","newpage2.php"),
array ("oldpage3.php","newpage3.php"),
array ("oldpage4.php","newpage4.php"),
array ("oldpage5.php","newpage5.php")
);
$input = "newpage5.php";
$result = [];
in_multiarray($input, $sample, $result);
print_r($result);
Path is stored in $result;
I have a :
$value = "val";
I also have an array :
$keys = ['key1', 'key2', 'key3'...]
The keys in that array are dynamically generated, and can go from 2 to 10 or more entries.
My goal is getting this :
$array['key1']['key2']['key3']... = $value;
How can I do that ?
Thanks
The easiest, and least messy way (ie not using references) would be to use a recursive function:
function addArrayLevels(array $keys, array $target)
{
if ($keys) {
$key = array_shift($keys);
$target[$key] = addArrayLevels($keys, []);
}
return $target;
}
//usage
$keys = range(1, 10);
$subArrays = addARrayLevels($keys, []);
It works as you can see here.
How it works is really quite simple:
if ($keys) {: if there's a key left to be added
$key = array_shift($keys); shift the first element from the array
$target[$key] = addArrayLevels($keys, []);: add the index to $target, and call the same function again with $keys (after having removed the first value). These recursive calls will go on until $keys is empty, and the function simply returns an empty array
The downsides:
Recursion can be tricky to get your head round at first, especially in complex functions, in code you didn't write, but document it well and you should be fine
The pro's:
It's more flexible (you can use $target as a sort of default/final assignment variable, with little effort (will add example below if I find the time)
No reference mess and risks to deal with
Example using adaptation of the function above to assign value at "lowest" level:
function addArrayLevels(array $keys, $value)
{
$return = [];
$key = array_shift($keys);
if ($keys) {
$return[$key] = addArrayLevels($keys, $value);
} else {
$return[$key] = $value;
}
return $return;
}
$keys = range(1, 10);
$subArrays = addARrayLevels($keys, 'innerValue');
var_dump($subArrays);
Demo
I don't think that there is built-in function for that but you can do that with simple foreach and references.
$newArray = [];
$keys = ['key1', 'key2', 'key3'];
$reference =& $newArray;
foreach ($keys as $key) {
$reference[$key] = [];
$reference =& $reference[$key];
}
unset($reference);
var_dump($newArray);
I have array:
$arr = array(
'a' => 1,
'b' => 2,
'c' => 3
);
Is there built-in php function which gets array value by key and then removes element from this array?
I know array_shift and array_pop functions, but I need to get element by custom key, not first/last element. Something like:
$arr; // 'a'=>1, 'b'=>2, 'c'=>3
$elem = array_shift_key($arr, 'b');
echo $elem; // 2
$arr; // 'a'=>1, 'c'=>3
Not sure about native way, but this should work:
function shiftByKey($key, &$array) {
if (!array_key_exists($key, $array)) {
return false;
}
$tmp = $array[$key];
unset($array[$key]);
return $tmp;
}
Edit: Updated it so that array is passed by reference. Else your array stays the same.
As far as I'm aware there's nothing that comes close to that. Can be written easily though:
function array_shift_key(array &$array, $key) {
$value = $array[$key];
unset($array[$key]);
return $value;
}
If you care, validate first whether the key exists in the array and do something according to your error handling philosophy if it doesn't. Otherwise you'll simply get a standard PHP notice for non-existing keys.
function array_shift_key(array &$input, $key) {
$value = null;
// Probably key presents with null value
if (isset($input[$key]) || array_key_exists($key, $input)) {
$value = $input[$key];
unset($input[$key]);
}
return $value;
}