Laravel's array_sort helper DESC e ASC - php

I want to sort a multidimensionl array by one or more keys using the Laravel helper array_sort.
array(
array('firstname_1','lastname_1'),
array('firstname_2','lastnmae_2')
)
I want to order it first by firstname and then by lastname.
I also want to do this in DESC or ASC order. How can I achieve this?
There are functions aivalable in the internet to do this but I would like to understand how to use the Laravel helper. The doc for array_sort (http://laravel.com/docs/helpers#arrays) I don't find comprehensive.

The array_sort() helper function is a very thin wrapper around the default Illuminate\Support\Collection::sortBy() method. Excluding comments, this is all that it does:
function array_sort($array, Closure $callback)
{
return \Illuminate\Support\Collection::make($array)->sortBy($callback)->all();
}
While handy, it is limiting in its sorting capabilities. Similarly, the Collection class will allow you to change sort direction, but not much else.
In my opinion, you have two options:
Skip a Laravel-only solution and use some normal PHP and array_multisort(). As #Jon commented, there's some great details in this SO question.
Use a combination of grouping and sorting in a Collection object to achieve the results you want.
I'd just stick with #1.

Just as an example how to sort by first name and then by last name with the sort helper of Laravel.
First define some more example data:
$array = array(
array('firstname_1','lastname_1'),
array('firstname_2','lastname_3'),
array('firstname_2','lastname_2'),
array('firstname_3','lastname_3'),
);
In our closure we want to sort by first name first, and then by last name. Laravel will sort by the returned values of the closure. So in your case the trick is to concatenate both strings:
$array = array_sort($array, function($value) {
return sprintf('%s,%s', $value[0], $value[1]);
});
Internally Laravel will now sort the contents of this intermediate array:
$intermediateArray = array(
'firstname_1,lastname_1',
'firstname_2,lastname_3',
'firstname_2,lastname_2',
'firstname_3,lastname_3',
);
This will result in an array which is sorted by first name and than by last name in ascending order.
To use descending order you need first sort the array and than reverse it:
$array = array_reverse(array_sort($array, function($value) {
return sprintf('%s,%s', $value[0], $value[1]);
}));

Related

why foreach print just one value in laravel?

I have a method:
public function winnerDetails()
{
$winners = DB::table('winners')->get();
//dd($winners);
foreach ($winners as $winner) {
$qrMainId = $winner->qrdetails_id;
}
dd($qrMainId);
}
dd($winners); returns to value of array but when i use foreach its returns one value. How can i get who value returns with foreach loop?
dd($winners) output:
and dd($qrMainId); return one value 44. But it should return another value of array 35; Thanks in advance.
To get array of ID's use
foreach ($winners as $winner) {
$qrMainId[]= $winner->qrdetails_id;
For just value use
$qrMainId='';
foreach ($winners as $winner) {
$qrMainId.= $winner->qrdetails_id;
If you want to accomplish this task in a Laravel way use the pluck method to map the array for the key that you want
<?php
public function winnerDetails()
{
$winnersId = DB::table('winners')->get()->pluck('qrdetails_id');
dd($winnersId);
}
I think you are not leveraging the power of Laravel here. Obviously, I don't know what you are trying to do but here are some pointers.
You should probably be making a model for the winners table now models in Laravel return Collections as does the DB facade you are using now. Now the Collection class contains alot of usefull helpers like pluck
At this point it becomes as easy as Winner::all()->pluck('id') and you got yourself an array of id's.
And if you would like to get them comma seperated or anything like that you can use the implode
And you would get Winner::all()->implode('id',',');
$qrMainId is a variable not an array.
It is modified in the foreach during every loop.
So your code has always the last element of the array.
Use an array to collect values like
$qrMainId[] = $winner->qrdetails_id;
or sql select directly the field you want.

Sorting array of objects by object variable, key being replaced

I have got an array of object instances, I need to sort these based on what a function within the object returns.
So basically my object has got 2 variables, the function will add them together, and then return the result, the list of objects need to be sorted based on this.
My sorting code:
function cmp($a, $b)
{
if ($a->calcPoints() == $b->calcPoints()) {
return 0;
}
return ($a->calcPoints() > $b->calcPoints()) ? -1 : 1;
}
usort($teamData, "cmp");
Without using the usort function I get the following when dumping my array:
Key: "Hull City FC" Value:
{"win":3,"draw":2,"loss":8,"goalFor":11,"goalConc":28} Key: "Leicester
City FC" Value: {"win":3,"draw":4,"loss":6,"goalFor":16,"goalConc":22}
Once I used the usort function, my keys are being replaced like so:
Key: 0 Value: {"win":10,"draw":1,"loss":2,"goalFor":29,"goalConc":10}
Key: 1 Value: {"win":9,"draw":3,"loss":1,"goalFor":29,"goalConc":12}
How can I stop this?
If you lookup usort() in the manual, you'll find:
Note: This function assigns new keys to the elements in array. It will remove any existing keys that may have been assigned, rather than
just reordering the keys.
uasort(), however, will: "Sort an array with a user-defined comparison function and maintain index association". Go ahead and try that, it should be what you're looking for.
You should use uasort function instead of usort. According to PHP documentation:
uasort — Sort an array with a user-defined comparison function and
maintain index association

Laravel lists function prepend value

Laravel lists function used to return an array so I could prepend a value. However it now is an object. What I would like to do is generate a list for a dropdown but add an extra value to the front that says something like:
['select a value.', '0']
How should I prepend data to the new Laravel lists function?
lists() returns a Collection object. To add an item to the beginning of the collection, you can use the prepend method:
$statusCollection = \App\Status::lists('name', 'id');
$statusCollection->prepend('select a value', 0);
Make sure you pass in a non-null key as the second parameter to prepend, otherwise this method will end up renumbering your numeric keys. If you don't provide a key, or pass in null, the underlying logic will use array_shift, which will renumber the keys. If you do provide a key, it uses an array union (+), which should preserve the keys.
For another option, you can get the underlying item array using the all() method and just do what you did before:
$statusCollection = \App\Status::lists('name', 'id');
$statusArray = $statusCollection->all();
// if you want to renumber the keys, use array_shift
array_unshift($statusArray, 'select a value');
// if you want to preserve the keys, use an array union
$statusArray = [0 => 'select a value'] + $statusArray;
If you are ok with the numeric keys being reindexed, then the accepted answer of using array_unshift() works. If you want to maintain the original numeric keys (for example, if the keys correspond to the id of your table entries), then you could do as follows:
$statusCollection = \App\Status::lists('name', 'id');
$statusArray = $statusCollection->all();
$finalArray = array('0' => 'Select a value') + $statusArray;

Remove matching items from object

I have an object named $list that is formatted like the following
{"name":"cats","title":"Cats"},{"name":"dogs","title":"Dogs"},{"name":"rabbits","title":"Hares"}
I have a second object named $list2 formatted like the following:
{"title":"Cats","name":"cats"},{"title":"Dogs","name":"dogs"},{"title":"Pigs","name":"pigs"},{"title":"Funny","name":"funny"},{"title":"Pictures","name":"pictures"},{"title":"Finance","name":"finance"},{"title":"Cars","name":"cars"},
I would like to go through $list and check if it is in $list2. If the name item is in $list2's name items then I would like to remove it from $list. Expected result in this case would be
{"name":"rabbits","title":"Hares"}
I have tried a few different looping techniques but I cannot seem to get it to match up correctly.
What you're looking for is the built-in array_udiff() function. It finds the difference in the arrays with a function that you provide.
$result = array_udiff($list1, $list2, function($a, $b) {
return strcasecmp($a['name'], $b['name']);
});
So this would return all objects that are not in $list2 but are in $list1. I'm assuming the name determines uniqueness.

PHP Iterator Custom Sort Order (Laravel 4)

I have a "Post" object, accessed via the IOC container. Various errors tell me this object's type ends up as a "Collection", which implements several interfaces, including IteratorAggregate and ArrayAccess.
I want to display a user-defined group of posts according to a specific order, e.g.:
$desired=array("69","63","70");//these represent post id's
Just sorting an array in this manner seems complex, but I want to sort my collection. I have been researching various combinations of usort(), uksort(), Eloquent's sortBy(), array_multisort()... but the most obvious solutions result in orders like 3,2,1 or 1,2,3, not 2,1,3.
The closest I have gotten to this goal is to fetch the ones I want,
//BlogController
private function myposts($desired){
$posts=$this->post->whereIn('id',$desired)->get();
...
"convert" the Collection object to an array,
$posts=$posts->toArray();
and treat the array with a custom function: source
function sortArrayByArray($array,$orderArray) {
$ordered = array();
foreach($orderArray as $key) {
if(array_key_exists($key,$array)) {
$ordered[$key] = $array[$key];
unset($array[$key]);
}
}
return $ordered + $array;
}
$sorted=sortArrayByArray($array1,$desired);
I can then vardump the array in the correct order, but since it is now an array, I can't access the $posts object in my view. Can I convert the array back into a post object?
This whole approach feels wasteful anyway, converting to an array and back... is it? Is there a more straightforward way of sorting the contents of a "Collection"?
This is a little better, perhaps (a native php function):
array_multisort($desired,$posts,SORT_STRING);
//only works with string keys
//$desire=(1,2,3) will not work!
Again, this works for arrays, but attempting directly on the "posts" object fails...
Finally, I discovered using a Presenter: https://github.com/robclancy/presenter#array-usage which works in the view, after one of the above is completed in the controller:
#foreach($posts as $post)
<?php $post=new PostPresenter($post) ?>
{{View::make('site.post.article')->with(compact('post'))}}
#endforeach
This finally works, but it still feels like a long way to do it. Is there a better way to accomplish this task? Performance concerns or best practices with one method vs. another? Thanks in advance for anyone able to help.
You can use the Collections own sort method and pass in a callback. The callback compares every two values in your collection tells the sort method which one is "higher". The sort method then sorts them accordingly. If you want a specific value order you just create mapping and sort by that. For more info check uasort
$posts->sort(function($a, $b){
$desired=array("69" => 0,"63" => 1,"70" =>2);
if(!array_key_exists($a,$desired) || !array_key_exists($b,$desired) || $a == $b){
return 0
}
else{
return ($desired[$a] < $desired[$b]) ? -1 : 1;
}
});
Alternatively to #tim's answer, you can re-assign the sorted array to a new Collection object:
$postsArray = $this->posts->toArray();
// Do some sorting/processing, then:
$newCollection = new \Illuminate\Database\Eloquent\Collection( $postsArray );

Categories