Sorting multidim array: prioritize if column contains substring, then order by a second column
Personally, I would use a custom (anonymous) function in conjunction with usort()
.
EDIT: Re - your comment. Hopefully this will put you on the right track. This function gives equal priority to elements which both have EN or neither have EN, or adjusted priority when just one has EN.
usort($array,function ($a, $b) {
$ac = strpos($a['countries'],'EN');
$bc = strpos($b['countries'],'EN');
if (($ac !== false && $bc !== false) || ($ac == false && $bc == false)) {
return 0;
}
elseif ($ac !== false) {
return 1;
}
else {
return -1;
}
});
This function, on the other hand, gives equal priority if both have EN, higher if one has EN, and does a text comparison if neither has EN.
usort($array,function ($a, $b) {
$ac = strpos($a['countries'],'EN');
$bc = strpos($b['countries'],'EN');
if ($ac !== false && $bc !== false)) {
return 0;
}
elseif ($ac !== false) {
return 1;
}
elseif ($bc !== false) {
return -1;
}
else {
if ($a['countries'] == $b['countries']) {
return 0;
}
elseif($a['countries'] > $b['countries']) {
return 1;
}
else {
return -1;
}
}
});
Again, hopefully this will give you enough direction to move forward on your own. If you are having any problems, feel free to post more comments and I'll try to help. A note if you're tying to compare multiple properties with weight: try out a funky switch block, e.g.
$ac = array_flip(explode(',',$a['countries']));
$bc = array_flip(explode(',',$b['countries']));
switch (true) {
case array_key_exists('EN',$ac) && !array_key_exists('EN',$bc):
return 1;
case array_key_exists('DE',$ac) && !array_key_exists('EN',$bc) && !array_key_exists('EN',$bc):
return 1;
// and so on
}
More Edits!
Actually, I was thinking more on the problem of complex sorting, and I have come up with the following solution, for your consideration. It will allow you to define numerical rankings based on keywords which would appear in the countries index. Here is the code, including an example:
Example Array
$array = array(
array(
'countries' => 'EN,DE,SP',
),
array(
'countries' => 'EN,CH,SP',
),
array(
'countries' => 'DE,SP,CH',
),
array(
'countries' => 'DE,SV,SP',
),
array(
'countries' => 'EN,SP,FR',
),
array(
'countries' => 'DE,FR,CH',
),
array(
'countries' => 'CH,EN,SP',
),
);
Sorting Routine
$rankings = array(
'EN' => 10,
'SP' => 8,
'FR' => 7,
'DE' => 5,
'CH' => 3,
'SV' => 1,
);
usort($array, function (&$a, &$b) use ($rankings) {
if (isset($a['_score'])) {
$aScore = $a['_score'];
}
else {
$aScore = 0;
$aCountries = explode(',',$a['countries']);
foreach ($aCountries as $country) {
if (isset($rankings[$country])) {
$aScore += $rankings[$country];
}
}
$a['_score'] = $aScore;
}
if (isset($b['_score'])) {
$bScore = $b['_score'];
}
else {
$bScore = 0;
$bCountries = explode(',',$b['countries']);
foreach ($bCountries as $country) {
if (isset($rankings[$country])) {
$bScore += $rankings[$country];
}
}
$b['_score'] = $bScore;
}
if ($aScore == $bScore) {
return 0;
}
elseif ($aScore > $bScore) {
return -1;
}
else {
return 1;
}
});
Note: This code will sort the highest ranking entires to the top of the array. If you want reverse behavior, change this:
elseif ($aScore > $bScore) {
to
elseif ($aScore < $bScore) {
Note that the greater-than was changed to a less-than symbol. Making this change will result in the lowest ranking entries being sorted to the top of the array. Hope all this helps!
NOTE ALSO!
This code will make a small change to your array, in that it adds the _score element to each array. Hopefully this is not a problem, as by storing this value I was literally able to increase speed by more than double (.00038-.00041 down to .00016-.00018 in my benchmarks). If not, remove the if
blocks that retrieve the cached value and let the contents of the else
blocks execute every time, except of course for the part which stores the score value.
By the way, here's a var_export()
dump of the array after it was sorted:
array (
0 => array (
'countries' => 'EN,SP,FR',
'_score' => 25,
),
1 => array (
'countries' => 'EN,DE,SP',
'_score' => 23,
),
2 => array (
'countries' => 'EN,CH,SP',
'_score' => 21,
),
3 => array (
'countries' => 'CH,EN,SP',
'_score' => 21,
),
4 => array (
'countries' => 'DE,SP,CH',
'_score' => 16,
),
5 => array (
'countries' => 'DE,FR,CH',
'_score' => 15,
),
6 => array (
'countries' => 'DE,SV,SP',
'_score' => 14,
),
)
Enjoy!
How does one sort a multi dimensional array by multiple columns in JavaScript?
The array literal []
is preferred over new Array
. The notation {0,4,3,1}
is not valid and should be [0,4,3,1]
.
Is there a need for reinventing the wheel? Two arrays can be joined using:
originalArray = originalArray.concat(addArray);
Elements can be appended to the end using:
array.push(element);
Arrays have a method for sorting the array. By default, it's sorted numerically:
// sort elements numerically
var array = [1, 3, 2];
array.sort(); // array becomes [1, 2, 3]
Arrays can be reversed as well. Continuing the previous example:
array = array.reverse(); //yields [3, 2, 1]
To provide custom sorting, you can pass the optional function argument to array.sort()
:
array = [];
array[0] = [1, "first element"];
array[1] = [3, "second element"];
array[2] = [2, "third element"];
array.sort(function (element_a, element_b) {
return element_a[0] - element_b[0];
});
/** array becomes (in order):
* [1, "first element"]
* [2, "third element"]
* [3, "second element"]
*/
Elements will retain their position if the element equals an other element. Using this, you can combine multiple sorting algoritms. You must apply your sorting preferences in reverse order since the last sort has priority over previous ones. To sort the below array by the first column (descending order) and then the second column (ascending order):
array = [];
array.push([1, 2, 4]);
array.push([1, 3, 3]);
array.push([2, 1, 3]);
array.push([1, 2, 3]);
// sort on second column
array.sort(function (element_a, element_b) {
return element_a[1] - element_b[1];
});
// sort on first column, reverse sort
array.sort(function (element_a, element_b) {
return element_b[0] - element_a[0];
});
/** result (note, 3rd column is not sorted, so the order of row 2+3 is preserved)
* [2, 1, 3]
* [1, 2, 4] (row 2)
* [1, 2, 3] (row 3)
* [1, 3, 3]
*/
To sort latin strings (i.e. English, German, Dutch), use String.localeCompare
:
array.sort(function (element_a, element_b) {
return element_a.localeCompare(element_b);
});
To sort date's from the Date
object, use their milliseconds representation:
array.sort(function (element_a, element_b) {
return element_a.getTime() - element_b.getTime();
});
You could apply this sort function to all kind of data, just follow the rules:
x
is the result from comparing two values which should be returned by a function passed to array.sort
.
x < 0
:element_a
should come beforeelement_b
x = 0
:element_a
andelement_b
are equal, the elements are not swappedx > 0
:element_a
should come afterelement_b
ksort a multidimensional array with two numeric keys
Just try with krsort
which sorts by keys in reversed order. You have also to loop over subarrays to sort them too because this function does not work on multidimentional arrays.
krsort($f);
foreach ($f as &$v) {
krsort($v);
}
Output:
array (size=2)
14 =>
array (size=5)
5 =>
array (size=1)
0 => string '' (length=0)
4 =>
array (size=1)
0 => string '' (length=0)
3 =>
array (size=1)
0 => string '' (length=0)
2 =>
array (size=1)
0 => string '' (length=0)
1 =>
array (size=1)
0 => string '' (length=0)
13 => &
array (size=5)
5 =>
array (size=1)
0 => string '' (length=0)
4 =>
array (size=1)
0 => string '' (length=0)
3 =>
array (size=1)
0 => string '' (length=0)
2 =>
array (size=1)
0 => string '' (length=0)
1 =>
array (size=1)
0 => string '' (length=0)
Sorting by key in a multidimensional array with php
Generic solution to sort arrays of arrays with multiple keys
Based on my answer to this question, here is a very generic solution that you can use in lots of situations.
Limitation: Requires PHP >= 5.3 to work, due to the presence of anonymous functions.
New and improved, now with descending sort support
function make_comparer() {
$criteriaNames = func_get_args();
$comparer = function($first, $second) use ($criteriaNames) {
// Do we have anything to compare?
while(!empty($criteriaNames)) {
// What will we compare now?
$criterion = array_shift($criteriaNames);
// Used to reverse the sort order by multiplying
// 1 = ascending, -1 = descending
$sortOrder = 1;
if (is_array($criterion)) {
$sortOrder = $criterion[1] == SORT_DESC ? -1 : 1;
$criterion = $criterion[0];
}
// Do the actual comparison
if ($first[$criterion] < $second[$criterion]) {
return -1 * $sortOrder;
}
else if ($first[$criterion] > $second[$criterion]) {
return 1 * $sortOrder;
}
}
// Nothing more to compare with, so $first == $second
return 0;
};
return $comparer;
}
How to use it
To sort by year ascending:
uasort($array, make_comparer('Year'));
To sort by year ascending, then by month ascending:
uasort($array, make_comparer('Year', 'Month'));
To sort by year descending, then by month ascending:
uasort($array, make_comparer(array('Year', SORT_DESC), 'Month'));
This last one is what you 're after.
PHP: Sorting a multidimensional array by an attribute
A usort
callback should return 3 types of values, depending on the circumstances:
- A negative number if parameter
$a
is less than$b
- A positive number if parameter
$b
is less than$a
- Zero if both
$a
and$b
are equal
usort($presentations, function($a, $b)
{
if($a['date'] == $b['date'])
{
return 0;
}
return $a['date'] < $b['date'] ? -1 : 1;
});
Sort grouped arrays by priority
You can
give each item an index, to know their original order. (It might also be possible to rely on sort stability, but it's easier to reason about when being explicit):
for (const [i, o] of array.entries()) o.index = i;
give each group a key to sort on, which is based on the max priority and first item:
const groupIds = Object.keys(groups).filter(g => g != 0);
groupIds.map(g => {
let minIndex = Infinity, maxPriority = 0;
for (const item of groups[g])
minIndex = Math.min(minIndex, item.index);
maxPriority = Math.max(maxPriority, priorities.get(item.priority));
}
return {group: groups[g], minIndex, maxPriority};
})add one single-item "group" per ungrouped item:
….concat(groups[0].map(item => {
return {group: [item], maxPriority: priorities.get(item.priority), minIndex: item.index};
}))before sorting the array of groups:
….sort((a, b) => b.maxPriority - a.maxPriority || a.minIndex - b.minIndex)
and then joining them together into the result:
….flatMap(({group}) => {
return group.sort((a, b) => a.index - b.index); // might be unnecessary depending on how you built the group arrays
});
Sorting a multidimensional array in php - by value
You need to create a user sort function (that is the most beautiful and fastest to program solution:
$usapopstats = array(
array('New York',8008278),
array('Los Angeles',3694820),
array('Chicago',2896016),
array('Houston',1953631),
array('Philadelphia',1517550),
array('Phonenix',1321045),
array('San Diego',1223400),
array('Dallas',1188580),
array('San Antonio',1144646),
array('Detroit',951270)
);
function sort_helper ($a, $b) {
if ($a[1] > $b[1]) return 1;
if ($a[1] == $b[1]) return 0;
return -1;
}
usort ($usapopstats, sort_helper);
var_dump($usapopstats);
This is not the fastest code, fine for a list of say up to 1000 records, but I wouldn't do it with an array with 100,000 entries. For each compare, the sort_helper function is being called, and since n log n compares are necessary, this means just as many function calls.
If you need this for a long list, encode the population in the key, and ksort:
$usapopstats = array(
array('New York',8008278),
array('Los Angeles',3694820),
array('Chicago',2896016),
array('Houston',1953631),
array('Philadelphia',1517550),
array('Phonenix',1321045),
array('San Diego',1223400),
array('Dallas',1188580),
array('San Antonio',1144646),
array('Detroit',951270)
);
$statshelper = array();
foreach($usapopstats as $index=>$stat){
$statshelper[$stat[1]."_".$index] = $stat; //also add the index to the key to avoid duplicates
}
ksort($statshelper, SORT_NUMERIC); //because the keys are strings (they contain an underscore) by default it will compare lexographically. The flag enforces numerical comparison on the part that can be converted to a number (i.e. the population)
$usapopstats = array_values($statshelper);
var_dump($usapopstats);
Javascript: sort multidimension array
Simple:
somearray.sort(function(a,b){
if (a[0]!=b[0]) return a[0]-b[0];
if (a[1]!=b[1]) return a[1]-b[1];
return a[2]-b[2];
});
Related Topics
Sort Multi-Dimensional Array by Specific Key
Differencebetween Sessions and Cookies in PHP
How to Identify Server Ip Address in PHP
How to Pass an Array via $_Get in PHP
Magic _Get Getter for Static Properties in PHP
Fastest Way to Retrieve a <Title> in PHP
Retrieve JSON Post Data in Codeigniter
Redirect with PHP After Ajax Call
Retrieve the Id of an Inserted Record: PHP & Ms SQL Server
Redirect to Page and Send Custom Http Headers
Autoloading Classes in PHPunit Using Composer and Autoload.Php
Verify Imagemagick Installation
Call to Undefined Function Apache_Request_Headers()
Convert Month from Name to Number
Is It Ever Ok to Store Password in Plain Text in a PHP Variable or PHP Constant
How to Write a Recursive Regex That Matches Nested Parentheses