How to match and relate data unique? - php
I'm having some difficulties figuring out how to return the best unique match, while assigning as many as possible.
Scenario: Each kid has a list of favorite fruits with a personal score. We only have ONE of each fruit, so we want to give it to the kid with the highest preference. One can be left without fruit if someone has a higher score, but we still want to give out as many fruits as possible.
The expected result would be:
0 = [1] Apple
1 = [0] Mango
2 = [0] Banana
3 = null
This is my input array:
Array
(
[0] => Array
(
[0] => Array
(
[name] => Banana
[score] => 80.2
)
[1] => Array
(
[name] => Apple
[score] => 40
)
)
[1] => Array
(
[0] => Array
(
[name] => Mango
[score] => 70
)
[1] => Array
(
[name] => Banana
[score] => 40
)
)
[2] => Array
(
[0] => Array
(
[name] => Banana
[score] => 90
)
[1] => Array
(
[name] => Orange
[score] => 20
)
)
[3] => Array
(
[0] => Array
(
[name] => Mango
[score] => 60
)
)
)
My approach first flattens your input into a simple 2D array allowing all rows to be sorted by score while preserving the fruit and childid data. After sorting, all rows are iterated (versus doing iterated full-array searches) and only stores the most preferred fruit, if available, for each child as requested.
OP's Input:
$input=[
[['name'=>'Banana','score'=>80.2],['name'=>'Apple','score'=>40]],
[['name'=>'Mango','score'=>70],['name'=>'Banana','score'=>40]],
[['name'=>'Banana','score'=>90],['name'=>'Orange','score'=>20]],
[['name'=>'Mango','score'=>60]]
];
Method:
$result=array_fill_keys(array_keys($input),null); // list all child ids and default to null
// flatten input array for simple sorting and iteration
foreach($input as $i=>$subarrays){
foreach($subarrays as $a){
$restructured[]=['score'=>$a['score'],'fruit'=>$a['name'],'childid'=>$i];
}
}
rsort($restructured); // will sort the array by score DESC
foreach($restructured as $a){
if(is_null($result[$a['childid']]) && !in_array($a['fruit'],$result)){
// only "fruitless" children wanting what is available
$result[$a['childid']]=$a['fruit'];
}
}
var_export($result);
Output:
array (
0 => 'Apple',
1 => 'Mango',
2 => 'Banana',
3 => NULL,
)
Related
Sorting multiple subarrays based on a total count from all sub arrays
I have some data which looks like this (reduced) Array ( [datasets] => Array ( [0] => Array ( [label] => NEW [backgroundColor] => #37fdfd [data] => Array ( [0] => 0 [1] => 0 [2] => 5 [3] => 0 ) ) [1] => Array ( [label] => Grade A [backgroundColor] => #76ef76 [data] => Array ( [0] => 8 [1] => 12 [2] => 11 [3] => 0 ) ) [2] => Array ( [label] => Grade B [backgroundColor] => #f9f96d [data] => Array ( [0] => 1 [1] => 6 [2] => 5 [3] => 3 ) ) [3] => Array ( [label] => Grade C [backgroundColor] => #f3ca36 [data] => Array ( [0] => 3 [1] => 0 [2] => 1 [3] => 4 ) ) [4] => Array ( [label] => Grade D [backgroundColor] => #f3ca36 [data] => Array ( [0] => 3 [1] => 0 [2] => 1 [3] => 0 ) ) ) [labels] => Array ( [0] => User 0 [1] => User 1 [2] => User 2 [3] => User 3 ) ) Here is a JSON string of the data (not reduced, numbers may differ slightly) {"datasets":[{"label":"NEW","backgroundColor":"#37fdfd","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"label":"Grade A","backgroundColor":"#76ef76","data":[9,14,12,0,4,17,13,0,10,0,18,18,12,13,13,4]},{"label":"Grade B","backgroundColor":"#f9f96d","data":[1,6,5,0,6,5,2,0,1,0,2,1,4,3,1,15]},{"label":"Grade C","backgroundColor":"#f3ca36","data":[3,0,1,0,2,0,0,0,0,0,1,1,0,0,0,0]},{"label":"Grade C","backgroundColor":"#f3ca36","data":[3,0,1,0,2,0,0,0,0,0,1,1,0,0,0,0]}],"labels":["User 0","User 1","User 2","User 3","User 4","User 5","User 6","User 7","User 8","User 9","User 10","User 11","User 12","User 13","User 14","User 15"]} Each dataset has an array of data which has keys that directly relates to a key in the labels array. This is currently sorted in alphabetical order by the label. This data structure is the structure required for Chart.js, which I am using to display a stacked bar chart on my webpage. Essentially what I need to accomplish is to sort the data array for every user in the labels array based on the sum of each data set for that user. I also need to sort the labels array to be in the same order. My original idea on how to achieve this is to create a temporary array, loop through all the data sets and add them to this temporary array in the order necessary, but I got stuck after calculating the total for each user. Here is my attempt: $return = []; foreach($calculated['labels'] as $key => &$name) { $total = 0; foreach($calculated['datasets'] as $dataset) { $total += $dataset['data'][$key]; } echo "$name - $total<br>"; } How can I sort my data and labels in descending order based on the total for each user from all datasets. Here is my expected output for the reduced data above Array ( [datasets] => Array ( [0] => Array ( [label] => NEW [backgroundColor] => #37fdfd [data] => Array ( [2] => 5 [1] => 0 [0] => 0 [3] => 0 ) ) [1] => Array ( [label] => Grade A [backgroundColor] => #76ef76 [data] => Array ( [2] => 11 [1] => 12 [0] => 8 [3] => 0 ) ) [2] => Array ( [label] => Grade B [backgroundColor] => #f9f96d [data] => Array ( [2] => 5 [1] => 6 [0] => 1 [3] => 3 ) ) [3] => Array ( [label] => Grade C [backgroundColor] => #f3ca36 [data] => Array ( [2] => 1 [1] => 0 [0] => 3 [3] => 4 ) ) [4] => Array ( [label] => Grade D [backgroundColor] => #f3ca36 [data] => Array ( [2] => 1 [1] => 0 [0] => 3 [3] => 0 ) ) ) [labels] => Array ( [2] => User 2 //23 total across all data sets [1] => User 1 //18 total across all data sets [0] => User 0 //15 total across all data sets [3] => User 3 //7 total across all data sets ) ) The key in the labels array acts as a unique identifier for each user in each dataset data array. Notice how each set of data inside of each dataset is in the same order, as is the labels array. Each set should be ordered by the total amount from all sets for each user, not necessarily the highest number in each dataset. For clarification, each set of data in each dataset contains a list of values, the key for each value is directly related to the key for each user in the labels array. So in my example, we have User 0 who has the key "0". This user has a total of 23 from adding up the values from each dataset with the key "0".
Complete solution: // get array $a = json_decode('{"datasets":[{"label":"NEW","backgroundColor":"#37fdfd","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"label":"Grade A","backgroundColor":"#76ef76","data":[9,14,12,0,4,17,13,0,10,0,18,18,12,13,13,4]},{"label":"Grade B","backgroundColor":"#f9f96d","data":[1,6,5,0,6,5,2,0,1,0,2,1,4,3,1,15]},{"label":"Grade C","backgroundColor":"#f3ca36","data":[3,0,1,0,2,0,0,0,0,0,1,1,0,0,0,0]},{"label":"Grade C","backgroundColor":"#f3ca36","data":[3,0,1,0,2,0,0,0,0,0,1,1,0,0,0,0]}],"labels":["User 0","User 1","User 2","User 3","User 4","User 5","User 6","User 7","User 8","User 9","User 10","User 11","User 12","User 13","User 14","User 15"]}', true); // get array of arrays with `data` key from each data set $users = array_column($a['datasets'], 'data'); // tricky code to sum arrays $sums = array_map('array_sum', array_map(null, ...$users)); // sort array with keeping keys arsort($sums); // we need flip so as `array_replace` will work as expected $keys = array_flip(array_keys($sums)); // "sorting" `data` subarrays foreach ($a['datasets'] as &$item) { $item['data'] = array_replace($keys, $item['data']); } // "sorting" `labels` subarray $a['labels'] = array_replace($keys, $a['labels']); // see the result print_r($a); Fiddle here https://3v4l.org/a7rPL
I see this task as a perfect candidate for array_multisort(). Your synchronously sorted subarrays don't need to retain their initial keys like in u_mulder's output. The first parameter must be the array of columnar sums, then the descending sort flag as the second parameter, then the labels subarray as a reference, then the dynamic number of data subarrays as references to the original array. Code: (Demo) $params = [[], SORT_DESC, &$array['labels']]; foreach ($array['datasets'] as ['data' => &$data]) { foreach ($data as $i => $d) { $params[0][$i] = ($params[0][$i] ?? 0) + $d; } $params[] = &$data; } array_multisort(...$params); var_export($array);
This looks like a typical job for map(reduce).sort: map each element to an object with id, so you can preserve "which user this used to be" information, and total, the result of reducing data. Then sort with a custom sort function (a,b) => a.total - b.total. E.g. function map_total($user, $pos) { return array( "id" => $pos, "total" => array_sum($user.data) ); } function cmp_total($a, $b) { return $a["total"] - $b["total"]; } $mapped = array_map("map_total", $thing.dataset, array_keys($thing.dataset)); $sorted = usort($mapped, "cmp_total");
Select array values based on another array
I have 2 arrays : “Array-List” and “Array-Criteria” : Array-List ( [1] => APPLE [2] => BANANA [3] => ORANGE [4] => LEMON ) Array-Criteria ( [0] => 1 [1] => 3 ) Is there a quick way (my Array-List can consist of thousands of entries) to select the values from Array-List based on Array-Criteria without looping through Array-List in PHP?
Use array_intersect_key and array_flip functions to get data as: $arr1 = Array-List ( [1] => APPLE [2] => BANANA [3] => ORANGE [4] => LEMON ) $arr2 = Array-Criteria ( [0] => 1 [1] => 3 ) var_dump(array_intersect_key($arr1, array_flip($arr2)));
If you loop over the criteria, you can build a list of the macthing items... $selected = []; foreach ( $arrayCriteria as $element ) { $selected[] = $arrayList[$element]; } Then $selected will be a list of the items your after. Also in a quick test, it's about twice as fast as using the array_ methods.
PHP array - count unique IDs
I have a PHP array that looks like this Array ( [0] => Array ( [events] => Array ( [0] => Array ( [label] => apple [id] => 3 ) [1] => Array ( [label] => onion [id] => 3 ) [2] => Array ( [label] => pear [id] => 2 ) [3] => Array ( [label] => orange [id] => 1 ) [4] => Array ( [label] => grape [id] => 41 ) ) ) ) I am trying to get a total count of unique IDs, so in the example above I would want to get a count of 4 Do I need to loop through the array or is there a function that can do it more efficiently? Currently it is a small data set but it could grow fairly large.
You can use array_column to get all the IDs, and array_unique to remove the duplicates, then count that. count(array_unique(array_column($array[0]['events'][0], 'id')))
One way or another you'll have to loop through it. I think most efficiently would be function getUnique($arr){ $val = array(); foreach($arr[0]["events"] as $v){ $val[$v["id"]] = true; } return count($val); }
PHP sort array alphabetically
I'm struggling on this one. I have an array that contains countries and regions. I want to sort both sets of information in ascending order on the key. Here is the array I'm working with: Array ( [Country] => Array ( [United Kingdom] => Array ( [London] => Array ( [0] => 1 [1] => 5 [2] => 23 [3] => 71 ) [Manchester] => Array ( [0] => 800 ) ) [United States] => Array ( [New York] => Array ( [0] => 147 [1] => 111 ) [Washington] => Array ( [0] => 213 ) [Florida] => Array ( [0] => 6 ) [Texas] => Array ( [0] => 9 ) ) [Brazil] => Array ( [Brasília] => Array ( [0] => 64 ) ) ) ) So the reordered array would be: Brazil - Brasília United Kingdom - London - Manchester United States - Florida - New York - Texas - Washington The data structure should remain the same, but the order of the number (e.g. London: 1,5,23,71) can stay the same. I've tried several of the sorting methods from: http://php.net/manual/en/array.sorting.php But they dont appear to do anything. Maybe because its a multidimensional array or maybe its not structured 100% logically... but I'm stuck with the array as it is.
You can try: ksort_recursive($data); print_r($data); Function Used function ksort_recursive(&$array) { ksort($array); foreach ( $array as &$a ) { is_array($a) && ksort_recursive($a); } } See Testing on Multiple PHP Versions
Step 1: Sort the country by key. ksort($arr['Country']); Step 2: Loop through the countries and sort those keys. foreach ($arr['Country'] as $country=>$data) { ksort($arr['Country'][$country]); }
php unique/group associative array
I would like to remove duplicates for the following array. I want to group by the first value and then rebuild the index. ie Array ( [0] => Array ( [title] => California [state_id] => 1 ) [1] => Array ( [title] => California [state_id] => 1 ) [2] => Array ( [title] => New Mexico [state_id] => 2 ) [3] => Array ( [title] => Washington [state_id] => 3 ) [4] => Array ( [title] => Montana [state_id] => 4 ) [5] => Array ( [title] => Montana [state_id] => 4 ) ) To Array ( [0] => Array ( [title] => California [state_id] => 1 ) [2] => Array ( [title] => New Mexico [state_id] => 2 ) [3] => Array ( [title] => Washington [state_id] => 3 ) [4] => Array ( [title] => Montana [state_id] => 4 ) ) and rebuild key Array ( [0] => Array ( [title] => California [state_id] => 1 ) [1] => Array ( [title] => New Mexico [state_id] => 2 ) [2] => Array ( [title] => Washington [state_id] => 3 ) [3] => Array ( [title] => Montana [state_id] => 4 ) )
$array = array_values(array_combine(array_map(function ($i) { return $i['title']; }, $array), $array)); I.e. extract the title key (array_map) use the extracted titles as keys for a new array and combine it with the old one (array_combine), which de-duplicates the array (keys have to be unique) run the result through array_values, which discards the keys Broken down: $keys = array_map(function ($i) { return $i['title']; }, $array); $deduped = array_combine($keys, $array); $result = array_values($deduped); For PHP 5.2- you'll need to write the anonymous callback function like this: array_map(create_function('$i', 'return $i["title"];'), $array)
Sort your array by title (usort does the trick) create a new empty array Cycle original array and store the value you find in the new empty array If next array value equals last then skip it At the end of this you have the array you want. There is another way: Sort your array by title Cycle original array If this array value equals last then unset it Sort the array again with PHP sort function to rebuild keys
$array = array_unique($array); Yes, it works with multi-dimentional arrays too; it just checks to equality.