How to sort array by array of order? - php

I have the following function that accepts array and order:
function toCSV($data, $order)
{
$res = [];
foreach ($data as $index => $row) {
$str = [];
foreach ($row as $column) {
foreach ($order as $key) {
if ($column['key'] == $key)
$str[] = $column;
}
}
$res[$index] = $str;
}
return $res;
}
The $data is:
[[{"key":"num","value":"5132756002760855","indexrow":162,"columnindex":0}...]]
The $order is array of sequance:
$order = ['num', 'mm', 'yy', 'zip']
So, I try to sort first array by the second array. But it does not work for me. As result I get unsorted array.
Full $data array is:
[[{"key":"undetected","value":"","indexrow":25,"columnindex":0}],[{"key":"num","value":"5354562740628987","indexrow":64,"columnindex":0},{"key":"mmyy","value":"01\/26","indexrow":64,"columnindex":1}]]

The if in the most inner loop will be true at some point and then the element is added, so the order doesn't change. Assuming you have only keys that appear in order. If you switch the two most inner loops it will work but it is not very efficient. O(nm) instead of O(n+m).
function toCSV($data, $order) {
$res = [];
foreach ($data as $index => $row) {
$map = [];
$str = [];
foreach ($row as $column) {
if (array_key_exists($column['key'], $map))
$map[$column['key']][] = $column;
else
$map[$column['key']] = [$column];
}
foreach ($order as $key) {
if (array_key_exists($key, $map))
$str = array_merge($str, $map[$key]);
}
$res[$index] = $str;
}
return $res;
}
If a key can only exist once per row then you can simplify and don't have to use arrays in the map. Simplified version, also assuming each key in order appears once per column:
function toCSV($data, $order) {
$res = [];
foreach ($data as $index => $row) {
$map = [];
$str = [];
foreach ($row as $column)
$map[$column['key']] = $column;
foreach ($order as $key)
$str[] = $map[$key];
// to create key if it doesn't exist use the following:
// $str[] = array_key_exists($key, $map) ? $map[$key] : [];
$res[$index] = $str;
}
return $res;
}
Both versions will discard columns with keys not in $order.

Related

Merging associative array with same values?

I have an associative array -
{
"1":{"list_price_9":"1250.0000","list_price_18":"1250.0000","golflan_price_9":"0.0000","golflan_price_18":"1250.0000"},
"2":{"list_price_9":"0.0000","list_price_18":"0.0000","golflan_price_9":"0.0000","golflan_price_18":"1250.0000"},
"3":{"list_price_9":"0.0000","list_price_18":"0.0000","golflan_price_9":"0.0000","golflan_price_18":"1250.0000"},
"4":{"list_price_9":"0.0000","list_price_18":"0.0000","golflan_price_9":"0.0000","golflan_price_18":"1250.0000"},
"5":{"list_price_9":"0.0000","list_price_18":"0.0000","golflan_price_9":"0.0000","golflan_price_18":"1250.0000"},
"6":{"list_price_9":"2500.0000","list_price_18":"2500.0000","golflan_price_9":"0.0000","golflan_price_18":"2500.0000"},
"7":{"list_price_9":"0.0000","list_price_18":"0.0000","golflan_price_9":"0.0000","golflan_price_18":"2500.0000"}
}
I want to convert the array such that the resulting array has merged the keys with similar values in a comma separated string.
So the result will be something like this -
{
"1":{"list_price_9":"1250.0000","list_price_18":"1250.0000","golflan_price_9":"0.0000","golflan_price_18":"1250.0000"},
"2,3,4,5,7":{"list_price_9":"0.0000","list_price_18":"0.0000","golflan_price_9":"0.0000","golflan_price_18":"1250.0000"},
"6":{"list_price_9":"2500.0000","list_price_18":"2500.0000","golflan_price_9":"0.0000","golflan_price_18":"2500.0000"}
}
This seems simple, but I am not being able to come up with an elegant solution for this.
Kindly help.
I tried something like this -
$common_prices = array();
foreach ($pricelist as $day => $prices) {
foreach ($common_prices as $new_day => $new_prices) {
if($prices === $new_prices) {
$modified_day = $new_day.','.$day;
$common_prices[$modified_day] = $new_prices;
unset($new_day);
}
}
$common_prices[$day] = $prices;
}
where $pricelist is the given array and $common_prices is the expected array. But obviously this will not work.
You can do it with linear complexity using intermediate array of accumulated keys for unique values:
$keys = [];
foreach($pricelist as $key=>$val) {
$str = json_encode($val);
if(!isset($keys[$str])) {
$keys[$str] = [];
}
$keys[$str][] = $key;
}
$common_prices = [];
foreach($keys as $key=>$val) {
$common_prices[join(',',$val)] = json_decode($key);
}
You can just aggregate keys and data in the separated arrays and then combine them.
Example:
$keys = [];
$data = [];
foreach ($pricelist as $day => $prices) {
if ($key = array_search($prices, $data))
$keys[$key] .= ',' . $day;
else {
$keys[] = $day;
$data[] = $prices;
}
}
$common_prices = array_combine($keys, $data);

Same position for duplicate values in a php array iterated with foreach loop

I have the following code that returns the indexed position of a value which its key matches the provided value in the parameter of the function($haystack).
$results = array("098"=>90,"099"=>89,"100"=>77,"101"=>77);
function getPosition($results,$StudentID){
arsort($results);
$index = 1;
$exists = '';
$keys = array_keys($results);
foreach($keys as $key)
{
if($key == $StudentID)
{
$score = $results[$key];
$position = $index;
}
$index++;
}
return $position;
}
echo getPosition($results,"098").'<br />';
echo getPosition($results,"099").'<br />';
echo getPosition($results,"100").'<br />';
echo getPosition($results,"101").'<br />';
The results are listed below:
90=1
89=2
77=4
77=3
Now my problem is:
1. I don't know how to get the function to return same position for two similar values(eg. 77);
edit: The StudentID parameter in the function is the key for array values.
eg. 098 is a key in the array and its the value for a particular StudentID
Simple return the positions as array.
$results = array("098"=>90,"099"=>89,"100"=>77,"101"=>77);
function getPosition($results,$StudentID)
{
arsort($results);
$index = 1;
$exists = '';
$keys = array_keys($results);
$position = array();
foreach($keys as $key)
{
if($key == $StudentID)
{
$score = $results[$key];
$position[] = $index;
}
$index++;
}
return $position;
}
print_r(getPosition($results,"77"));
Should you be searching for values rather than keys?
$results = array("098"=>90,"099"=>89,"100"=>77,"101"=>77);
function getPosition($results, $StudentID) {
$index = 1;
$indexes = array();
foreach ($results as $key=>$value) {
if ($value == $StudentID) $results[] = $index;
$index++;
}
return $indexes;
}
print_r(getPosition($results, "77"));

PHP Flatten Array with multiple leaf nodes

What is the best way to flatten an array with multiple leaf nodes so that each full path to leaf is a distinct return?
array("Object"=>array("Properties"=>array(1, 2)));
to yield
Object.Properties.1
Object.Properties.2
I'm able to flatten to Object.Properties.1 but 2 does not get processed with recursive function:
function flattenArray($prefix, $array)
{
$result = array();
foreach ($array as $key => $value)
{
if (is_array($value))
$result = array_merge($result, flattenArray($prefix . $key . '.', $value));
else
$result[$prefix . $key] = $value;
}
return $result;
}
I presume top down will not work when anticipating multiple leaf nodes, so either need some type of bottom up processing or a way to copy array for each leaf and process (althought that seems completely inefficient)
function flatten(array $data, $separator = '.') {
$result = array();
$stack = array();
$path = null;
reset($data);
while (!empty($data)) {
$key = key($data);
$element = $data[$key];
unset($data[$key]);
if (is_array($element)) {
if (!empty($data)) {
$stack[] = array($data, $path);
}
$data = $element;
$path .= $key . $separator;
} else {
$result[$path . $key] = $element;
}
if (empty($data) && !empty($stack)) {
list($data, $path) = array_pop($stack);
}
}
return $result;
}
var_dump(flatten(array("Object"=>array("Properties"=>array(1, 2)))));
Output:
array(2) {
["Object.Properties.0"]=>
int(1)
["Object.Properties.1"]=>
int(2)
}
Use function flatMapAssoc() from Kdyby Framework:
$flattened= array();
flatMapAssoc($array, function ($value, $keys) use (&$flattened) {
$flattened[implode('.', $keys)] = $value;
});
/**
* #param array|\Traversable $array
* #param callable $callback
* #return array
*/
function flatMapAssoc($array, $callback)
{
$callback = callback($callback);
$result = array();
$walker = function ($array, $keys = array()) use (&$walker, &$result, $callback) {
foreach ($array as $key => $value) {
$currentKeys = $keys + array(count($keys) => $key);
if (is_array($value)) {
$walker($value, $currentKeys);
continue;
}
$result[] = $callback($value, $currentKeys);
}
return $result;
};
return $walker($array);
}
I would use a wrapper function to hide implementation details (the prefix parameter)
and added an if branch to test for empty arrays. At last, in case of simple leaf you should use the $value variable and not the $key one.
$x = array("Object"=>array("Properties"=>array(1, 2), "test"=>array(), "post"));
function flatten ($array) {
return flattenArray('',$array);
}
function flattenArray($prefix, $array) {
$result = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
if(count($value)) {
$result = array_merge($result, flattenArray($prefix."$key.", $value));
} else {
$result[] = "$prefix$key";
}
} else {
$result[] = "$prefix$value";
}
}
return $result;
}
echo join("\n", flatten($x));
If you want to mimic a tree structure, maybe you can use a different array structure. Something like this:
$y = array ("Object",
array("Properties", 1, 2),
"test",
"post"
);
and flattenArray becomes:
function flattenArray($prefix, $array) {
$result = array();
$prefix .=array_shift($array).'.';
foreach ($array as $value) {
if (is_array($value)) {
$result = array_merge($result, flattenArray($prefix, $value));
} else {
$result[] = "$prefix$value";
}
}
return $result;
}

PHP function to get recursive path keys with path

Given an array, I would like a flattened version of the array keys. Each array key would need the 'path' of the array, to that point, appended with an underscore.
An example explains this best.
$arr = array("location"=>0,"details"=>array("width"=>0,"height"=>0,"level"=>array("three"=>0)));
function answer($arr) {....}
the answer function would return this:
array("location","details_width","details_height","details_level_three");
UPDATE:
Here is the work in progress. It will accept an array and return the array keys, but with no depth:
function recursive_keys($input)
{
$output = array_keys($input);
foreach($input as $sub){
if(is_array($sub)){
$output = array_merge($output, recursive_keys($sub));
}
}
return $output;
}
$ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));
$results = array();
foreach ($ritit as $leafValue) {
$path = array();
foreach (range(0, $ritit->getDepth()) as $depth) {
$path[] = $ritit->getSubIterator($depth)->key();
}
$results[] = join('_', $path);
}
function recursive_keys(array $array, array $path = array()) {
$result = array();
foreach ($array as $key => $val) {
$currentPath = array_merge($path, array($key));
if (is_array($val)) {
$result = array_merge($result, recursive_keys($val, $currentPath));
} else {
$result[] = join('_', $currentPath);
}
}
return $result;
}
Demo here: http://codepad.viper-7.com/WQ3UYI

How to get values in an array this way with PHP?

From:
$arr = array(array('key1'=>'A',...),array('key1'=>'B',...));
to:
array('A','B',..);
$output = array();
foreach ($arr as $array_piece) {
$output = array_merge($output, $array_piece);
}
return array_values($output);
On the other hand, if you want the first value from each array, what you want is...
$output = array();
foreach ($arr as $array_piece) {
$output[] = array_unshift($array_piece);
}
But I'm thinking you want the first one.
Relatively simple conversion by looping:
$newArray = array()
foreach ($arr as $a) {
foreach ($a as $key => $value) {
$newArray[] = $value;
}
}
Or, perhaps more elegantly:
function flatten($concatenation, $subArray) {
return array_merge($concatenation, array_values($subArray));
}
$newArray = array_reduce($arr, "flatten", array());
John's solution is also nice.
Something like this should work
<?
$arr = array(array('key1'=>'A','key2'=>'B'),array('key1'=>'C','key2'=>'D'));
$new_array = array();
foreach ($arr as $key => $value) {
$new_array = array_merge($new_array, array_values($value));
}
var_export($new_array);
?>
If you want all the values in each array inside your main array.
function collapse($input) {
$buf = array();
if(is_array($input)) {
foreach($input as $i) $buf = array_merge($buf, collapse($i));
}
else $buf[] = $input;
return $buf;
}
Above is a modified unsplat function, which could also be used:
function unsplat($input, $delim="\t") {
$buf = array();
if(is_array($input)) {
foreach($input as $i) $buf[] = unsplat($i, $delim);
}
else $buf[] = $input;
return implode($delim, $buf);
}
$newarray = explode("\0", unsplat($oldarray, "\0"));

Categories