This question already has answers here:
Possible to pass a closure to usort in PHP?
(2 answers)
How can I sort arrays and data in PHP?
(14 answers)
Closed 2 years ago.
EDIT: I rewrote my post in order to be clearer and provide a standalone case with real values (no Ajax anymore).
I have 2 arrays that are exactly identical except that one has the same values but cleaned (html, special chars, etc..).
I would like to evaluate the sorting against "arrayClean" but to sort "arrayOriginal" instead (not arrayClean) according to that evaluation.
So, this is what I have:
<?php
$arrayOriginal = array(
array('id' => '100','surface' => '<span>300</span>','whatever' => 'qSDqsd'),
array('id' => '5465','surface' => '100 ch','whatever' => 'ghjkghjk'),
array('id' => '40489','surface' => '<b>1000</b>','whatever' => 'fgsdfg')
);
$arrayClean = array(
array('id' => '100','surface' => '300','whatever' => 'qSDqsd'),
array('id' => '5465','surface' => '100','whatever' => 'ghjkghjk'),
array('id' => '40489','surface' => '1000','whatever' => 'fgsdfg')
);
usort($arrayOriginal, function($a, $b) use (&$arrayClean) {
return $a['surface'] < $b['surface'];
});
echo '<pre>'; print_r($arrayOriginal); echo '</pre>';
?>
here is what I get (which is wrong as the arrayClean doesn't seem to be taken into account for the sorting) :
Array
(
[0] => Array
(
[id] => 100
[surface] => <span>300</span>
[whatever] => qSDqsd
)
[1] => Array
(
[id] => 40489
[surface] => <b>1000</b>
[pwhatever] => fgsdfg
)
[2] => Array
(
[id] => 5465
[surface] => 100 ch
[whatever] => ghjkghjk
)
)
But if I use arrayClean alone, just to check if the sorting script is right:
usort($arrayClean, function($a, $b) {
return $a['surface'] < $b['surface'];
});
echo '<pre>'; print_r($arrayClean); echo '</pre>';
Then the result is what I expect it to be:
Array
(
[0] => Array
(
[id] => 40489
[surface] => 1000
[whatever] => fgsdfg
)
[1] => Array
(
[id] => 100
[surface] => 300
[whatever] => qSDqsd
)
[2] => Array
(
[id] => 5465
[surface] => 100
[whatever] => ghjkghjk
)
)
So it seems that evaluating arrayClean but sorting arrayOriginal accordingly doesn't work. It only evaluates AND sort arrayOriginal.
Do I use "use()" wrong ? Should I use something else ?
Thank you.
Assuming both arrays are sorted by a common factor (i.e. id).
uksort($arrayOriginal, function($a, $b) use ($arrayClean) {
return $arrayClean[$a]['surface'] < $arrayClean[$b]['surface'];
});
Emphasising once more, to make it work, both arrays MUST contain elements in the same order. In your case elements of both arrays MUST come in the following order (by id): 100, 5465, 40489
BUT I'd rather do something like:
usort($arrayOriginal, function($a, $b) {
return yourSurfaceCleanMethod($arrayOriginal['surface']) < yourSurfaceCleanMethod($arrayOriginal['surface']);
});
All depends on your needs, of course, but if you are using $arrayClean only as a reference for sorting original array and you have that yourSurfaceCleanMethod handy, I'd definitely do the above.
I think the arrayClean is not needed if your arrayOriginal should be sorted according to the content of the HTML tags.
strip_tags() can be used for this in your sort function.
usort($arrayOriginal, function($a, $b) {
return strip_tags($b['surface']) <=> strip_tags($a['surface']);
});
Note: Use the spaceship operator <=> to get correct comparison results 1, 0 and -1.
I accepted Nemoden's answer as a courtesy because technically it answered the question according to its scope (the test case as presented). But here is the solution I found that works best for me :
$arrayOriginal = array(
array('id' => '100','surface' => '<span>300</span>','whatever' => 'qSDqsd'),
array('id' => '5465','surface' => '100 ch','whatever' => 'ghjkghjk'),
array('id' => '40489','surface' => '<b>1000</b>','whatever' => 'fgsdfg')
);
$arrayClean = array(
array('id' => '100','surface' => '300','whatever' => 'qSDqsd'),
array('id' => '5465','surface' => '100','whatever' => 'ghjkghjk'),
array('id' => '40489','surface' => '1000','whatever' => 'fgsdfg')
);
$surface = array_column($arrayClean, 'Surface');
array_multisort($surface, SORT_DESC, SORT_REGULAR, $arrayOriginal);
And then I get this output, which is EXACTLY what I want:
Array
(
[0] => Array
(
[id] => 40489
[surface] => <b>1000</b>
[whatever] => fgsdfg
)
[1] => Array
(
[id] => 100
[surface] => <span>300</span>
[whatever] => qSDqsd
)
[2] => Array
(
[id] => 5465
[surface] => 100 ch
[whatever] => ghjkghjk
)
)
The answer of Nemoden was very helpful BUT it gave me this, for whatever reason:
Array
(
[1] => Array
(
[id] => 40489
[surface] => <b>1000</b>
[whatever] => fgsdfg
)
[0] => Array
(
[id] => 100
[surface] => <span>300</span>
[whatever] => qSDqsd
)
[2] => Array
(
[id] => 5465
[surface] => 100 ch
[whatever] => ghjkghjk
)
)
The sub arrays are PRINTED in the right order but the sub arrays indexes weren't changed. So when exporting to javascript "arrayOriginal", it would come back to its initial sorting because the sub arrays would be re-ordered according to their indexes again. At least, that's what I experienced.
Anyway, now the problem is solved and the answers I got here were very helpful.
Thank you.
Related
I'm struggling to sort my array of objects into alphabetical order by the title key. Getting error that 'title' is undefined.
I want to go from this:
My Array
(
[0] => Array
(
[area] => TACT
[pages] => Array
(
[0] => Array
(
[id] => 1484
[title] => Registry
)
[1] => Array
(
[id] => 1385
[title] => Education
To:
My Array
(
[0] => Array
(
[area] => TACT
[pages] => Array
(
[0] => Array
(
[id] => 1385
[title] => Education
)
[1] => Array
(
[id] => 1484
[title] => Registry
Have tried:
function sort_alpha_title($a, $b) {
return strnatcmp($a['title'], $b['title']);
}
usort($myArray, 'sort_alpha_title');
You should use
function sort_alpha_title($a, $b) {
return strnatcmp($a['title'], $b['title']);
}
usort($myArray[0]['pages'], 'sort_alpha_title');
Because the title property is a child property of the pages property.
$myArray = array(
array(
'area' => 'TACT',
'pages' => array(
array(
'id' => 1484,
'title' => 'Registry'
),
array(
'id' => 1385,
'title' => 'Education'
)
)
)
);
function sort_alpha_title($a, $b)
{
return strnatcmp($a["title"], $b["title"]);
}
usort($myArray[0]['pages'], 'sort_alpha_title');
var_dump($myArray);
Sry for the doubled answer but i cant add a comment because of my reputation.
tomhuckle wrote
thanks! this works. But only for one of my objects in the array. Sorry the description should of said. ive tried putting a for each loop but that's not working. here's what i tried: foreach($myArray as $a) { usort($a['pages'], 'sort_alpha_title'); }
you dont need the foreach-loop (at the end of your comment) because its already "looped" within one call.
best regards
Array
array (
[0] => array
(
[field1] => 'name'
[field2] => 'text'
[field3] => array
(
[0] => array
(
[text] => 'This arrays order is to be kept!'
[name] => 'Only sort the sub-sub-array below!'
[order] => ''
)
)
)
[1] => array
(
[field1] => 'name'
[field2] => 'text'
[field3] => array
(
[0] => array
(
[text] => '2014'
[name] => 'Dummy!'
[order] => '1'
)
[1] => array
(
[text] => '2013'
[name] => 'Try me!'
[order] => '2'
)
[2] => array
(
[text] => '1985'
[name] => 'Help!'
[order] => '5'
)
[3] => array
(
[text] => '2002'
[name] => 'Please!'
[order] => '4'
)
[4] => array
(
[text] => '2007'
[name] => 'Order!'
[order] => '3'
)
)
)
...
Goal
I want to keep the 1st and 2nd level array and it's values as is. I need to sort the keys in the 2nd sub-sub-array by the value in theorder-field. So where it says the years, that sub-sub-array should be sorted ASC or DESC. and all other keys/values/structure/map should be kept as is.
Sorted array:
array (
[0] => array
(
[field1] => 'name'
[field2] => 'text'
[field3] => array
(
[0] => array
(
[text] => 'This arrays order is to be kept!'
[name] => 'Only sort the sub-sub-array below!'
[order] => ''
)
)
)
[1] => array
(
[field1] => 'name'
[field2] => 'text'
[field3] => array
(
[0] => array
(
[text] => '2014'
[name] => 'Dummy!'
[order] => '1'
)
[1] => array
(
[text] => '2013'
[name] => 'Try me!'
[order] => '2'
)
[2] => array
(
[text] => '2007'
[name] => 'Order!'
[order] => '3'
)
[3] => array
(
[text] => '2002'
[name] => 'Please!'
[order] => '4'
)
[4] => array
(
[text] => '1985'
[name] => 'Help!'
[order] => '5'
)
)
)
Help
I've spent hours and hours of trying different custom solutions, mostly based on usort() but I've also done it knowing that none of those Q&A were describing the situation I've got.
I'm starting to doubt that it's even possible.
Does anyone know if it is? And if so, can you please shed some light or provide a hint/tutorial/example for demonstration?
Laravel 5.1
I'm retrieving this from a Laravel Model and using toArray();
Is there is a better way to achieve this in Laravel I would be glad to know.
If the sub-subarray you are wanting to sort is always identified by the same key, you can just usort the contents of that key.
foreach ($test as &$value) {
// Using a (^) reference to each value will sort the existing
// array instead of creating a sorted copy.
usort($value['field3'], function($a, $b) {
// You can use an anonymous function like this for the usort comparison function.
if ($a['order'] < $b['order']) return -1;
if ($a['order'] == $b['order']) return 0;
// In case of 'order' ties, you may want to add a comparison of a different key
return 1; // $a['order'] must be > $b['order'] at this point
});
}
Note that the comparison function used in usort:
must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
It may be better to do this using Laravel, because the database will most likely be able to do this sorting faster than PHP will, but we would need to know much more about your model setup to figure that out and that sounds like basically a totally different question.
If I understood the problem correctly, this could be a solution (in these examples $arr is the variable holding the array):
function cmp($a, $b)
{
return ($a['order'] < $b['order']) ? -1 : 1;
}
foreach($arr as $key => $subarr){
foreach($subarr as $key2 => $subsubarr){
if(is_array($subsubarr)){
usort($subsubarr,"cmp");
$subarr[$key2] = $subsubarr;
}
}
$arr[$key] = $subarr;
}
or
function cmp($a, $b)
{
return ($a['order'] < $b['order']) ? -1 : 1;
}
foreach($arr as &$subarr){
foreach($subarr as &$subsubarr){
if(is_array($subsubarr)){
usort($subsubarr,"cmp");
}
}
}
Racking my brain on this, found many examples of similar situations however the solutions don't seem to match up.
I have two arrays being built as the result of SQL queries on different databases.
One is coming through formatted as such:
$data = array([$sku] => array(['LocalSKU'] => $sku,
['Price'] => $msrp,
['Price2'] => $wholesale,
['Price3'] => $distributor,
['Price4'] => $map))
The other array is formatted as such:
$matchme = array([0] => array(['entity_id'] => $entity_id,
['sku'] => $sku,
['type_id'] => $type_id))
Currently, I can get the individual data to match up via:
echo $matchme[0]['sku'];
echo $matchme[0]['entity_id'];
echo $matchme[0]['type_id'];
echo $data[$matchme[0]['sku']]['Price'];
echo $data[$matchme[0]['sku']]['Price2'];
echo $data[$matchme[0]['sku']]['Price3'];
echo $data[$matchme[0]['sku']]['Price4'];
However, when I try and merge the matching rows in both arrays, I get an empty array. The $data array contains 74 unique $sku, and $matchme is the result of checking those $sku's against a database and returning an array with 61 elements. So, the combined array should have 61 elements with matched pricing data based on the $sku.
How I am attempting to build the combined array is below, can anyone point me towards what I am doing wrong?
foreach($matchme as $key){
if(in_array($matchme[$key]['sku'], $data)){
$matched_luggage[$matchme[$key]['sku']][] = array(
'sku' => $matchme[$key]['sku'],
'entity_id' => $matchme[$key]['entity_id'],
'type_id' => $matchme[$key]['type_id'],
'MSRP' => $data[$matchme[$key]['sku']]['Price'],
'Wholesale' => $data[$matchme[$key]['sku']]['Price2'],
'Distributor' => $data[$matchme[$key]['sku']]['Price3'],
'MAP' => $data[$matchme[$key]['sku']]['Price4']
);
}
}
In the above example, evaluate $key as 0, and the value of ['sku'] are matching.
------------------------Edited-------------------------
Per request, here is the result of print_r($data) truncated for space:
Array
(
[12PK-TITANIUM-CR123A] => Array
(
[LocalSKU] => 12PK-TITANIUM-CR123A
[Price] => 11.76
[Price2] => 10.32
[Price3] => 0
[Price4] => 0
)
[AA-CLAMSHELL] => Array
(
[LocalSKU] => AA-CLAMSHELL
[Price] => 0.25
[Price2] => 0
[Price3] => 0
[Price4] => 0
)
[AAA-CLAMSHELL] => Array
(
[LocalSKU] => AAA-CLAMSHELL
[Price] => 0.25
[Price2] => 0
[Price3] => 0
[Price4] => 0
)
[AE-AEL280PI] => Array
(
[LocalSKU] => AE-AEL280PI
[Price] => 0
[Price2] => 0
[Price3] => 0
[Price4] => 0
) )
Per request, here is the result of print_r($matchme) truncated for space:
Array
(
[0] => Array
(
[entity_id] => 693
[sku] => 12PK-TITANIUM-CR123A
[type_id] => simple
)
[1] => Array
(
[entity_id] => 2596
[sku] => AE-AEL480HL
[type_id] => simple
)
[2] => Array
(
[entity_id] => 2597
[sku] => AE-AEL600-T6
[type_id] => simple
)
[3] => Array
(
[entity_id] => 2598
[sku] => AE-AEWL2
[type_id] => simple
) )
Per request, here is the desired result of $matched_luggage:
$matched_luggage = array( [12PK-TITANIUM-CR123A] => array([sku] => 12PK-TITANIUM-CR123A,
[entity_id] => 693,
[type_id] => simple,
[Price] => 11.76,
[Price2] => 10.32,
[Price3] => 0,
[Price4] => 0))
with an additional array per matched sku.
Try this:
foreach ($matchme as $arrProduct) {
if (isset($data[$arrProduct['sku']])) {
$arrMerged[$arrProduct['sku']]=array_merge($arrProduct, $data[$arrProduct['sku']]);
}
}
print_r($arrMerged);
The reason your code doesn't work is here:
if(in_array($matchme[$key]['sku'], $data)) [...]
What in_array() does is tell you whether your needle (in your case the SKU string) exists as a value of array haystack (in your case, $data). You are essentially trying to match a string to an array, rather than another string.
What you really want is just to match the SKU string to the key of $data, for which isset() is probably the simplest approach.
Assuming your first array as $first and second array as $second
foreach ($first as $key => $each) {
foreach ($second as $secondeach) {
if($secondeach['sku'] == $key) {
$first[$key] = array_merge($first[$key], $secondeach);
// unset since you do not want LocalSKU value anymore.
unset($first[$key]['LocalSKU']);
}
}
}
$first is your array you wanted.
This Stackoverflow answer is 95% of the way there: https://stackoverflow.com/a/10484863. I just need to pass another variable into the function which I believe uses closure.
I want to pass the variable sort:
$sort = get_category_name();
into:
function compareByName($a, $b) {
return strcmp($a["name"], $b["name"]);
}
usort($a, 'compareByName');
so its $a[$sort] and not defined by me as $a[key].
My array:
Array(
[0] => Array
(
[id] => 7
[product_type_id] => 2
[category_en] => Prints
[category_es] => Impresiones
[category_ru] => Печати
)
[1] => Array
(
[id] => 8
[product_type_id] => 2
[category_en] => Drawings
[category_es] => Dibujos
[category_ru] => Рисунки
)
[2] => Array
(
[id] => 9
[product_type_id] => 2
[category_en] => Paintings
[category_es] => Pinturas/Cuadros
[category_ru] => Картины
)
)
should allow me to sort the array by the category based on the users language which is set by get_category_name().
i have wrote a script to produce an array of data but now want to display in order of score. The array outputs as follows;
[display_name] => Array
(
[0] => ACT_Web_Designs
[1] => user1_design
[2] => user2_design
)
[proffesion] => Array
(
[0] => Web Developer
[1] => web developer
[2] => Web Developer
)
[score] => Array
(
[0] => 15
[1] => 6
[2] => 15
)
[img] => Array
(
[0] => ./?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic
[1] =>
[2] =>
)
so in a nutshell I am wanting it to be converted as follows;
[display_name] => Array
(
[0] => ACT_Web_Designs
[1] => user2_design
[2] => user1_design
)
[proffesion] => Array
(
[0] => Web Developer
[1] => web developer
[2] => Web Developer
)
[score] => Array
(
[0] => 15
[1] => 15
[2] => 6
)
[img] => Array
(
[0] => ./?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic
[1] =>
[2] =>
)
I have been looking at asort() but cant get anything to work. any help would be much appreciated.
This is exactly where the PHP's array_multisort comes to use. It is a case where you want to sort many arrays based on the comparison happening in just one of them.
I've modified the array score to have distinct values.
<?php
$arr = array(
'display_name' => array('ACT_Web_Designs','user1_design','user2_design'),
'proffesion' => array('Web Developer','web developer','web developer'),
'score' => array(12,6,15),
'img' => array('./?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic','','')
);
var_dump($arr);
array_multisort($arr['score'], SORT_ASC, SORT_NUMERIC,
$arr['display_name'],
$arr['proffesion'],
$arr['img']
);
var_dump($arr);
?>
Here goes a working demo.
How about this simpler one
$arr = array("k"=>array("A","B","C"),"l"=>array(15,6,15),"n"=>array("k","l","n"));
array_multisort($arr["k"],SORT_NUMERIC,SORT_DESC,$arr["l"],$arr["n"]);
var_dump($arr);
Doesn't it work to just rsort the score array?
rsort($data['score'], SORT_NUMERIC);
I have managed to do this, i was just after a more efficient way;
$array = array(
'display_name' => array(0 => 'ACT_Web_Designs', 1 => 'user1_design', 2 => 'user2_design' ),
'proffesion' => array( 0 => 'Web Developer', 1 => 'web developer', 2 => 'Web Developer' ),
'score' => array( 0 => 15, 1 => 6, 2 => 15 ),
'img' => array( 0 => './?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic', 1 => '', 2 => '' )
);
arsort($array['score'], SORT_NUMERIC );
foreach($array['score'] as $key => $val ) {
$newarray['display_name'][] = $array['display_name'][$key];
$newarray['proffesion'][] = $array['proffesion'][$key];
$newarray['score'][] = $array['score'][$key];
$newarray['img'][] = $array['img'][$key];
}
print_r($newarray);
returns
Array
(
[display_name] => Array
(
[0] => ACT_Web_Designs
[1] => user2_design
[2] => user1_design
)
[proffesion] => Array
(
[0] => Web Developer
[1] => Web Developer
[2] => web developer
)
[score] => Array
(
[0] => 15
[1] => 15
[2] => 6
)
[img] => Array
(
[0] => ./?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic
[1] =>
[2] =>
)
)
Use rsort()
<?php
$fruits = array("lemon", "orange", "banana", "apple");
rsort($fruits);
reset($fruits);
while (list($key, $val) = each($fruits)) {
echo "$key = $val\n";
}
?>
This example would display:
0 = orange
1 = lemon
2 = banana
3 = apple
The most elegant solution that I could find would not reorder the data structure but merely access it in a different fashion.
$scores = $data['score'];
arsort($scores);
$keys_ordered_by_score = array_keys($scores);
Now you can, say, grab the display_name and "proffesion" that has the highest score by the following:
$first_place = $keys_ordered_by_score[0];
echo $data['display_name'][$first_place],
' is a ', $data['proffesion'][$first_place];
Of course, if you really need to reorder the data structure, this idea is useless to you. Any of the other answers using array_multisort() will likely suit that need.
This will not re-sort them for you, but it will let you go through them in the order you want. You can reassign them if you want, but I'd just use this for the output order.
Edit: This will not work, due to the possibility of non-unique key values. See Comments below, and learn from my mistake
$sort_order = $array['score'];
arsort($sort_order);
$sort_order = array_flip($sort_order);
foreach($sort_order as $key){
echo $array['display_name'][$key].' - '.$array['score'][$key];
}