Sorting Multidim Array: Prioritize If Column Contains Substring, Then Order by a Second Column

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.

  1. x < 0: element_a should come before element_b
  2. x = 0: element_a and element_b are equal, the elements are not swapped
  3. x > 0: element_a should come after element_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



Leave a reply



Submit