I have a single array like:
array (
'0' => 1,
'1' => 3,
'2' => 4
)
and I want to turn it to a multi-dimensional array like:
array (
'1' =>
array (
'3' => '4'
),
)
Another single array :
array (
'0' => 'a',
'1' => 'b',
'2' => 'c',
'3' => 'd'
)
to :
array (
'a' =>
array (
'b' =>
array (
'c' => 'd'
),
),
),
How can I achieve this?
Note:
The array is dynamic, not always containing 3 or 4 elements.
There are multiple ways to achieve this:
Simple for loop
First, use array_pop() to pop (i.e. store and remove) the last value from the array. Then iterate from the end of the array, successively creating an array with the values (via array_values()):
function transformArray($input) {
$accumulator = array_pop($input);
$values = array_values($input);
for($i = count($input)-1; $i > -1; $i--) {
$accumulator = array($values[$i] => $accumulator);
}
return $accumulator;
}
See it demonstrated in this playground example
foreach loop
Using the code from the previous example, the for loop can be replaced with a foreach loop by utilizing array_reverse(). That way we don't have to worry about when to start and stop the index values, index in to the array, etc.
function transformArray($input) {
$accumulator = array_pop($input);
$values = array_reverse(array_values($input));
foreach($values as $value) {
$accumulator = array($value => $accumulator);
}
return $accumulator;
}
See it demonstrated in this playground example.
Functional approach: array_reduce()
Array_reduce() can be used to simplify the iteration. The first argument of array_reduce() is the input array, the second is a callback function and the third is the initial value. The parameters for the callback function are the accumulator value (from previous iterations) and the array element at the current array index). The initial value is used as the accumulator value for the first iteration, so we can pass the value that was popped from the end of the array.
function transformArray($input) {
$accumulator = array_pop($input);
$values = array_reverse(array_values($input));
return array_reduce($values, function($accumulator, $element) {
return array($element => $accumulator);
}, $accumulator);
}
See it demonstrated in this playground example.
Recursive Approach
Perhaps the simplest technique is to use a recursive function by employing array_shift() to take the first element off the array and return an array with that element as the key and the value is the return value of calling the same function, unless there is only one item, in which case that one item is returned.
function transformArray($input) {
if (count($input) == 1) {
return $input[0];
}
return array(array_shift($input) => transformArray($input));
}
See it demonstrated in this playground example.
If it has always 3 elements:
$temp = array (
'0' => 1,
'1' => 3,
'2' => 4
);
$result = array();
$result[$temp['0']] = array($temp['1'] => $temp['2']);
Although I think there are better strategies, it's possible to use eval() for this purpose:
<?php
/* Example array */
$arr1 = array(
'0' => 'a',
'1' => 'b',
'2' => 'c',
'3' => 'd'
);
$array_string = '';
/* Build array string */
for($i = 0; $i < count($arr1); $i++){
if($i < (count($arr1) - 1)){
$array_string .= 'array ( \''. $arr1[$i] .'\' => ';
} else {
$array_string .= ' \''. $arr1[$i] .'\'';
}
}
/* Close array string */
for($y = 0; $y < count($arr1); $y++){
if($y < (count($arr1) - 2)){
$array_string .= ' ), ';
} else {
$array_string .= ' )';
break;
}
}
/* Evaluate array string as PHP code and put it in $result */
$result = eval("return $array_string;");
/* Print the result */
echo '<pre>';
print_r($result);
echo '</pre>';
?>
Result:
Array
(
[a] => Array
(
[b] => Array
(
[c] => d
)
)
)
But as I said, keep this quote in mind:
If eval() is the answer, you're almost certainly asking the wrong question. -- Rasmus Lerdorf, BDFL of PHP
Ask yourself why you need this. Is there another strategy available for what you want?
Related
I have an associative array like this [this array is combination of two different arrays]:
I combine arrays and via array_combine() function
$products = array_combine($one_array_key,$second_array_values);
$products = array(
"arn1" =>"A",
"arn2" =>"A",
"arn3" =>"A",
"arn4" =>"B",
"arn5" =>"B",
"arn6" =>"B"
);
So As you can see there are two distinct values from array A and B.
I want two arrays consists of it's keys.
Or in other words: Compare values of associative array and matched values's key will be extract to the other array .
So My expected out is:
$A = array("arn1","arn2","arn3");
$B = array("arn4","arn5","arn6");
My code:
$products = array_combine($one_array_key,$second_array_values);
$products_distinct_values = array_count_values($product);
$products_distinct_values_keys = array_keys($products_distinct_values);
foreach ($products as $key => $value)
{
// what should I need write there, to get the x numbers of array(s), containing *key* of the same *value of array*
}
I SUPER would never use this "variable variables" technique in a professional project, but it does satisfy your brief. I will urge you to find the folly in your XY Problem.
Effectively, the snippet below will synchronously iterate over the two related input arrays and create variably-named result arrays to push values into.
Code: (Demo)
$one_array_key = ['A', 'A', 'A', 'B', 'B', 'B'];
$second_array_values = ['arn1', 'arn2', 'arn3', 'arn4', 'arn5', 'arn6'];
foreach ($one_array_key as $index => $value) {
$$value[] = $second_array_values[$index];
}
var_export($A);
echo "\n---\n";
var_export($B);
Output:
array (
0 => 'arn1',
1 => 'arn2',
2 => 'arn3',
)
---
array (
0 => 'arn4',
1 => 'arn5',
2 => 'arn6',
)
It makes much more sense to have a statically named variable containing keys and related subarrays. This way you can call array_keys() on the result, if you wish, to find out which groups were found in the original input.
Code: (Demo)
$result = [];
foreach ($one_array_key as $index => $value) {
$result[$value][] = $second_array_values[$index];
}
var_export($result);
Output:
array (
'A' =>
array (
0 => 'arn1',
1 => 'arn2',
2 => 'arn3',
),
'B' =>
array (
0 => 'arn4',
1 => 'arn5',
2 => 'arn6',
),
)
You can use the following PHP code to do that.
<?php
$products = array("arn1"=>"A", "arn2"=>"A", "arn3"=>"A", "arn4"=>"B", "arn5"=>"B", "arn6"=>"B");
$A = array();
$B = array();
foreach ($products as $key => $value)
{
if ($value == "A")
{
array_push ($A, $key);
}
else if ($value == "B")
{
array_push ($B, $key);
}
}
print_r ($A);
print_r ($B);
?>
I want to add an element to an array at random postion using a loop
I have a fixed ranks like the following
$ranks=array("10","9","8","7","6","5","4","3","2","1");
And I have a random rank position according to a chain,
$agent_ranks=array("10","6","2","1");
which are missing indices
I have calculated the difference between the arrays
$arr_diff=array("9","8","7","5","4","3");
Now I want a dynamic array as a result:
$arr_diff_new=array("0","9","8","7","0","5","4","3","0","0");
How can I add value="0" at the missing indices?
<?php
$ranks=array("10","9","8","7","6","5","4","3","2","1");
$agent_ranks= array_flip( array("10","6","2","1") );
foreach( $ranks as $k=>$v ) {
if ( isset($agent_ranks[$v]) ) {
$ranks[$k] = 0;
}
}
var_export($ranks);
prints
array (
0 => 0,
1 => '9',
2 => '8',
3 => '7',
4 => 0,
5 => '5',
6 => '4',
7 => '3',
8 => 0,
9 => 0,
)
see also: array_flip
You can do it using in_array and for loop:
$ranks=array("10","9","8","7","6","5","4","3","2","1");
$agent_ranks=array("10","6","2","1");
for($i=0;$i < count($ranks); $i++){
if(in_array($ranks[$i], $agent_ranks)){
$newarray[$i] = 0;
}else{
$newarray[$i] = $ranks[$i];
}
}
print_r($newarray);
You can also use in_array in if clause to check if rank is int $agent_ranks and then push 0 or old rank value to new array
$arr_diff_new = array();
foreach($ranks as $rank){
array_push($arr_diff_new,(in_array($rank,$agent_ranks))?0:$rank);
}
You can use array function array_map,
<?php
$array1=array("10","9","8","7","6","5","4","3","2","1");
print_r(array_map('filter',$array1));
function filter($a){
$array2=array("9","8","7","5","4","3");
if(in_array($a,$array2)){
return $a;
}else{
return 0;
}
}
?>
Simply using in_array with foreach loop like as
$ranks=array("10","9","8","7","6","5","4","3","2","1");
$agent_ranks=array("10","6","2","1");
$result = array();
foreach($ranks as $key => $value){
$result[] = in_array($value,$agent_ranks) ? 0 : $value;
}
print_r($result);
Is there a simpler way to get all array keys that has same value, when the value is unknown.
The problem with array_unique is that it returns the unique array and thus it doesn't find unique values.
That is, for example, from this array:
Array (
[a]=>1000
[b]=>1
[c]=>1000
)
I want to get this
Array (
[a]=>1000
[c]=>1000
)
Another way around this is, if I could find the lonely values, and then their keys, and then use array_diff
This is what I've got so far, looks awful:
$a = array( 'a' => 1000, 'b' => 1, 'c' => 1000 );
$b = array_flip( array_count_values( $a ) );
krsort( $b );
$final = array_keys( $a, array_shift( $b ) );
Update
Using Paulo Freites' answer as a code base, I could get it working pretty easily, maintainable and easy on eyes kind of way… by using the filtering as a static class method I can get the duplicate values from an array by just calling ClassName::get_duplicates($array_to_filter)
private static $counts = null;
private static function filter_duplicates ($value) {
return self::$counts[ $value ] > 1;
}
public static function get_duplicates ($array) {
self::$counts = array_count_values( $array );
return array_filter( $array, 'ClassName::filter_duplicates' );
}
Taking advantage of closures for a more straightforward solution:
$array = array('a' => 1000, 'b' => 1, 'c' => 1000);
$counts = array_count_values($array);
$filtered = array_filter($array, function ($value) use ($counts) {
return $counts[$value] > 1;
});
var_dump($filtered);
This gave me the following:
array(2) {
["a"]=>
int(1000)
["c"]=>
int(1000)
}
Demo: https://eval.in/67526
That's all! :)
Update: backward-compatible solution
$array = array('a' => 1000, 'b' => 1, 'c' => 1000);
$counts = array_count_values($array);
$filtered = array_filter($array, create_function('$value',
'global $counts; return $counts[$value] > 1;'));
var_dump($filtered);
Demo: https://eval.in/68255
Your implementation has a few issues.
1) If there are 2 of value 1000 and 2 of another value, the array_flip will lose one of the sets of values.
2) If there are more than two different values, the array_keys will only find the one value that occurs most.
3) If there are no duplicates, you will still bring back one of the values.
Something like this works always and will return all duplicate values:
<?php
//the array
$a = array( 'a' => 1000, 'b' => 1, 'c' => 1000 );
//count of values
$cnt = array_count_values($a);
//a new array
$newArray = array();
//loop over existing array
foreach($a as $k=>$v){
//if the count for this value is more than 1 (meaning value has a duplicate)
if($cnt[$v] > 1){
//add to the new array
$newArray[$k] = $v;
}
}
print_r($newArray);
http://codepad.viper-7.com/fal5Yz
If you want to get the duplicates in an array try this:
array_unique(array_diff_assoc($array1, array_unique($array1)))
I found this from:
http://www.php.net/manual/en/function.array-unique.php#95203
at the moment I cant figure out another solution...
// target array
$your_array = array('a'=>1000, 'b'=>1, 'c'=>1000);
// function to do all the job
function get_duplicate_elements($array) {
$res = array();
$counts = array_count_values($array);
foreach ($counts as $id=>$count) {
if ($count > 1) {
$r = array();
$keys = array_keys($array, $id);
foreach ($keys as $k) $r[$k] = $id;
$res[] = $r;
}
}
return sizeof($res) > 0 ? $res : false;
}
// test it
print_r(get_duplicate_elements($your_array));
output:
Array
(
[0] => Array
(
[a] => 1000
[c] => 1000
)
)
example #2: - when you have different values multiplied
// target array
$your_array = array('a'=>1000, 'b'=>1, 'c'=>1000, 'd'=>500, 'e'=>1);
// output
print_r(get_duplicate_elements($your_array));
output:
Array
(
[0] => Array
(
[a] => 1000
[c] => 1000
)
[1] => Array
(
[b] => 1
[e] => 1
)
)
if function result has been assigned to $res variable $res[0] gets an array of all elements from original array with first value found more than once, $res[1] gets array of elements with another duplicated-value, etc... function returns false if nothing duplicate has been found in argument-array.
Try this
$a = array( 'a' => 1, 'b' => 1000, 'c' => 1000,'d'=>'duplicate','e'=>'duplicate','f'=>'ok','g'=>'ok' );
$b = array_map("unserialize", array_unique(array_map("serialize", $a)));
$c = array_diff_key($a, $b);
$array = array("1"=>"A","2"=>"A","3"=>"A","4"=>"B","5"=>"B","6"=>"B");
$val = array_unique(array_values($array));
foreach ($val As $v){
$dat[$v] = array_keys($array,$v);
}
print_r($dat);
This question already has answers here:
Get min and max value in PHP Array
(9 answers)
Closed 2 years ago.
The Problem
I have a multidimensional array similar to the one below. What I'm trying to achieve is a way to find and retrieve from the array the one with the highest "Total" value, now I know there's a function called max but that doesn't work with a multidimensional array like this.
What I've thought about doing is creating a foreach loop and building a new array with only the totals, then using max to find the max value, which would work, the only issue would then be retrieving the rest of the data which relates to that max value. I'm not sure that's the most efficient way either.
Any ideas?
Array
(
[0] => Array
(
[Key1] => Key1
[Total] => 13
)
[1] => Array
(
[Key2] => Key2
[Total] => 117
)
[2] => Array
(
[Key3] => Key3
[Total] => 39
)
)
Since PHP 5.5 you can use array_column to get an array of values for specific key, and max it.
max(array_column($array, 'Total'))
Just do a simple loop and compare values or use array_reduce. # is an error suppressor; it hides the fact that $a['total'] is not declared before it is accessed on the first iteration. Demo
$data = array_reduce($data, function ($a, $b) {
return #$a['Total'] > $b['Total'] ? $a : $b ;
});
print_r($data);
// Array( [Key2] => Key2 [Total] => 117 )
It could also be written with arrow function syntax which has been avaiable since PHP7.4. Demo
var_export(
array_reduce(
$data,
fn($result, $row) =>
$result['Total'] > $row['Total']
? $result
: $row,
['Key1' => null, 'Total' => PHP_INT_MIN]
)
);
// array ('Key2' => 'Key2', 'Total' => 117,)
It's so basic algorithm.
$max = -9999999; //will hold max val
$found_item = null; //will hold item with max val;
foreach($arr as $k=>$v)
{
if($v['Total']>$max)
{
$max = $v['Total'];
$found_item = $v;
}
}
echo "max value is $max";
print_r($found_item);
Working demo
I know this question is old, but I'm providing the following answer in response to another question that pointed here after being marked as a duplicate. This is another alternative I don't see mentioned in the current answers.
I know there's a function called max but that doesn't work with a multidimensional array like this.
You can get around that with array_column which makes getting the maximum value very easy:
$arr = [['message_id' => 1,
'points' => 3],
['message_id' => 2,
'points' => 2],
['message_id' => 3,
'points' => 2]];
// max value
$max = max(array_column($arr, 'points'));
Getting the associative key is where it gets a little more tricky, considering that you might actually want multiple keys (if $max matches more than one value). You can do this with an anonymous function inside array_map, and use array_filter to remove the null values:
// keys of max value
$keys = array_filter(array_map(function ($arr) use ($max) {
return $arr['points'] == $max ? $arr['message_id'] : null;
}, $arr));
Output:
array(1) {
[0]=>
int(1)
}
If you do end up with multiples keys but are only interested in the first match found, then simply reference $keys[0].
another simple method will be
$max = array_map( function( $arr ) {
global $last;
return (int)( ( $arr["Total"] > $last ) ? $arr["Total"] : $last );
}, $array );
print_r( max( $max ) );
<?php
$myarray = array(
0 => array(
'Key1' => 'Key1',
'Total' => 13,
),
1 => array(
'Key2' => 'Key2',
'Total' => 117,
),
2 => array(
'Key2' => 'Key3',
'Total' => 39,
),
);
$out = array();
foreach ($myarray as $item) {
$out[] = $item['Total'];
}
echo max($out); //117
unset($out, $item);
Can be done using array_walk(array_walk_recursive if needed)
$arr is the array you want to search in
$largestElement = null;
array_walk($arr, function(&$item, $key) use (&$largestElement) {
if (!is_array($largestElement) || $largestElement["Total"] < $item["Total"]) {
$largestElement = $item;
}
});
You can use php usort function:
http://php.net/manual/en/function.usort.php
A pretty illustrative example is given here:
<?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";
}
?>
So it will sort the max value to the last array index.
Output:
$fruits[0]: apples
$fruits[1]: grapes
$fruits[2]: lemons
This example is given on aforementioned link
array_reduce accepts a 3rd "initial" parameter. Use this to avoid the bad practice of using "#" error suppression :
$data = array_reduce($data, function ($a, $b) {
return $a['Total'] > $b['Total'] ? $a : $b ;
},['Total' => 0]);
print_r($data);
PHP 7.4
$data = array_reduce($data, fn(a,b) => $a['Total'] > $b['Total'] ? $a : $b, ['Total' => 0]);
I'm trying to do something but I can't find any solution, I'm also having some trouble putting it into works so here is a sample code, maybe it'll be enough to demonstrate what I'm aiming for:
$input = array
(
'who' => 'me',
'what' => 'car',
'more' => 'car',
'when' => 'today',
);
Now, I want to use array_splice() to remove (and return) one element from the array:
$spliced = key(array_splice($input, 2, 1)); // I'm only interested in the key...
The above will remove and return 1 element (third argument) from $input (first argument), at offset 2 (second argument), so $spliced will hold the value more.
I'll be iterating over $input with a foreach loop, I know the key to be spliced but the problem is I don't know its numerical offset and since array_splice only accepts integers I don't know what to do.
A very dull example:
$result = array();
foreach ($input as $key => $value)
{
if ($key == 'more')
{
// Remove the index "more" from $input and add it to $result.
$result[] = key(array_splice($input, 2 /* How do I know its 2? */, 1));
}
}
I first though of using array_search() but it's pointless since it'll return the associative index....
How do I determine the numerical offset of a associative index?
Just grabbing and unsetting the value is a much better approach (and likely faster too), but anyway, you can just count along
$result = array();
$idx = 0; // init offset
foreach ($input as $key => $value)
{
if ($key == 'more')
{
// Remove the index "more" from $input and add it to $result.
$result[] = key(array_splice($input, $idx, 1));
}
$idx++; // count offset
}
print_R($result);
print_R($input);
gives
Array
(
[0] => more
)
Array
(
[who] => me
[what] => car
[when] => today
)
BUT Technically speaking an associative key has no numerical index. If the input array was
$input = array
(
'who' => 'me',
'what' => 'car',
'more' => 'car',
'when' => 'today',
'foo', 'bar', 'baz'
);
then index 2 is "baz". But since array_slice accepts an offset, which is not the same as a numeric key, it uses the element found at that position in the array (in order the elements appear), which is why counting along works.
On a sidenote, with numeric keys in the array, you'd get funny results, because you are testing for equality instead of identity. Make it $key === 'more' instead to prevent 'more' getting typecasted. Since associative keys are unique you could also return after 'more' was found, because checking subsequent keys is pointless. But really:
if(array_key_exists('more', $input)) unset($input['more']);
I found the solution:
$offset = array_search('more', array_keys($input)); // 2
Even with "funny" arrays, such as:
$input = array
(
'who' => 'me',
'what' => 'car',
'more' => 'car',
'when' => 'today',
'foo', 'bar', 'baz'
);
This:
echo '<pre>';
print_r(array_keys($input));
echo '</pre>';
Correctly outputs this:
Array
(
[0] => who
[1] => what
[2] => more
[3] => when
[4] => 0
[5] => 1
[6] => 2
)
It's a trivial solution but somewhat obscure to get there.
I appreciate all the help from everyone. =)
$i = 0;
foreach ($input as $key => $value)
{
if ($key == 'more')
{
// Remove the index "more" from $input and add it to $result.
$result[] = key(array_splice($input, $i , 1));
}
$i++;
}