PHP sort 2d array by index (non-associative) - php

This code does not run properly, but it suggests what I am trying to do:
function sort_2d_by_index($a,$i) {
function cmp($x, $y) {
// Nested function, can't find $i
// (global $i defeats the purpose of passing an arg)
if ($x[$i] == $y[$i]) { return 0; }
return ($x[$i] < $y[$i]) ? -1 : 1;
}
usort($a,"cmp");
return $a;
}
There HAS to be a much better way to do this. I've been examining ksort(), multisort(), and all sorts of sorts until I'm sort of tired trying to sort it all out.
The situation is this: I've got a 2-d array...
array(
array(3,5,7),
array(2,6,8),
array(1,4,9)
);
...and I want to sort by a column index. Say, column [1], would give this result:
array(
array(1,4,9),
array(3,5,7),
array(2,6,8)
);
Does someone have a link (I'm sure this has been asked before), or could someone say "you need foosort, definitely". Thanks very much.

In the documentation of array_multisort it is mentioned that it can be used for this kind of thing.
You can't avoid creating an array that consists of only one column:
$sort_column = array();
foreach ($a as $row)
$sort_column []= $row[1]; // 1 = your example
array_multisort($sort_column, $a);
This sorts both arrays synchronously so that afterwards your whole array is sorted in the same order as the $sort_column array is.
As of PHP 5.3 you can use a closure (pass $i into the function) by defining your cmp function like this:
$cmp = function($x, $y) use ($i) { ... };

You can use use to access $i:
function cmp($x, $y) use ($i) {
// $i now available
if ($x[$i] == $y[$i]) { return 0; }
return ($x[$i] < $y[$i]) ? -1 : 1;
}

http://www.php.net/manual/en/function.sort.php#99419
phpdotnet at m4tt dot co dot uk
Simple function to sort an array by a specific key. Maintains index association.

Related

how to access the current index in array_reduce?

Having read this SO post, In PHP I am aware that you can get the index under iteration with array_map as such:
array_map(function($item, $index) { ... }, $items, array_keys($items));
How can I get the an $index available to me when I use array_reduce? I have tried:
array_reduce($items, array_keys($items), function($acc, $item, $index) { ... }, array());
array_reduce($items, function($acc, $item, $index) { ... }, array(), array_keys($items));
But I still can't seem to get $index in an array_reduce. Has anyone successfully done this before?
EDIT
Here's some context as to why I am asking this question.
I do not want to use foreach because I would have to mutate an array outside of the foreach in order to create my collection. I would prefer to avoid mutation.
Other languages allow one to use reduce and get access to the current index like in JavaScript and Ruby. I was hoping to get the same feature in PHP. Oh well! Looks like I'm going to have to use a foreach to create my array while also having the current index under iteration.
I just had same issue. It's pretty simple to solve.
$i = 0;
$p = array_reduce($array, function($output, $item) use (&$i) {
// use $i
// do something with $item and/or $output
$i++; // increment $i
return $output;
}, $initial);
&$i pass $i as reference and grants it will be updated.
<?php
$data = ['one', 'two', 'three'];
$result = array_reduce($data, function($carry, $item){
$carry['out'] .= $carry['i'] . ') '. $item . '; ';
$carry['i']++;
return $carry;
}, ['out' => '', 'i' => 1] )['out'];
echo $result; // 1) one; 2) two; 3) three;
Arrays in PHP are peculiar things: they can be used as lists, queues, dictionaries, sets, ordered dictionaries, and all sorts of other multi-valued structures. However, most functions are written with one or two of those structures in mind.
In the case of array_reduce, the array is treated as a list - an ordered collection of items. As such, the keys of the array are not handed to the callback. This makes sense for common cases, like calculating a total, or an average, etc.
There are undoubtedly cases where you want to instead reduce an ordered dictionary of key-value pairs; unfortunately, PHP does not provide a function for that.
On the other hand, there are two related functions which might be usable instead:
array_map, which runs the callback on each element and produces a new array with one item of output for item of input
array_walk, which runs the callback on each element and ignores the result, but which can take a third parameter by reference where side-effects can be accumulated
All three functions can also trivially be implemented with a foreach loop, so you could write your own reduce function something like this (untested):
function array_reduce_assoc(array $array, callable $callback, $initial=null) {
$carry = $initial;
foreach ( $array as $key => $value ) {
$carry = $callback($carry, $key, $value);
}
return $carry;
}
You already know you how to get the index in array_map, so you could use that to reduce instead if you like. Just use a reference to the "carry" variable in the callback.
$example = ['a' => 1, 'b' => 2, 'c' => 3];
array_map(function($key, $value) use (&$reduced) {
$reduced .= "$key$value"; // for example
}, array_keys($example), $example);
echo $reduced; //a1b2c3
I'm not sure I see the advantage of this over foreach, but it's another possible way to do it.
What you're aware about the getting index in array_reduce doesn't exist. Check documentation.
The question is why?
Look, reduce is being used for the purpose of getting down to one single value, it's aggregation. You have initial value, and next value, show your reduce strategy. There is no point in knowing current item index.
In some other languages, they might provide current index, but not for this array_reduce in PHP. It's language and function designed, there are some constraints in implementation.
But it doesn't mean you can't have it. You can have it, but not through callback!
$i = 0;
$data = [1, 2, 3, 4];
// (1)
$result = array_reduce($data, function ($c, $i) { return $c + $i; }, 0);
// (2)
array_reduce($data, function ($c, $i) { echo "$i\n"; $i++; return $c + $i; }, 0);
The thing is, the function is designed to do this. Or you're trying to use a wrong function. Some other functions might be what you're looking for.
Or you can write a function that do it, too :)
This is similar to another answer, but differs on a couple of points.
First, the other answer provides an example, but not a complete one. Also the
other answer defines the callback arguments as $carry, $key, $value. I feel
that $carry, $value, $key would be better, as it would match
JavaScript reduce.
It would also keep the order of PHP’s own array_reduce, while just adding
an extra argument. For my example I am implementing array_flip, just to
demonstrate what is possible:
<?php
$a = ['May', 'June'];
$m = [];
$f = function ($m_acc, $s_cur, $n_idx) {
return $m_acc + [$s_cur => $n_idx];
};
foreach ($a as $n => $s) {
$m = $f($m, $s, $n);
}
print_r($m);

Reducing a multi-dimensional array

I have an array that carries a definite number of dimensions so I'm not really looking at something recursive (Unless maybe for maintainability sake in the future). It's a numeric array gotten from the database with each row holding another array. Each of those level 2 arrays contain strings like
var1, var2 , var3
And so on. Note the irregular appearance of commas in the string. So I intend to break the comma delimited string in the third level then log them in the final array but I get an error saying I am supplying an null array. So I want to know why it says the array is null and how I can make it recognise that as a valid array. My code goes below:
function fetch_each($arr) {
$temp = array();
for ($i = 0; $i < count($arr); $i++) {
for ($j = 0; $j < count($arr[$i]); $j++) {
array_reduce(preg_split("/[\s,]+/", $arr[$i][$j]), function($a, $b) {
return array_push($temp, $a, $b);
});
}
}
return $temp;
}
PS: Please don't mark as duplicate. I don't want to copy someone else's code but want to understand why this does not work. Thanks.
You have this problem because $temp is not visible in the function block.
To solve that, you must use the keyword use (variable_name) next to the function definition as in this example :
array_reduce(preg_split("/[\s,]+/", $arr[$i][$j]), function($a, $b) use (&$temp) {
return array_push($temp, $a, $b);
});
Just a remark, $a will contain the result of array_push
Returns:int the new number of elements in the array.
So you can remove it from the array_push() instruction to keep a clean array with only splitted strings

Sort array with uasort

I’m working on a little PHP function that will sort an array based off of if the item returned isVerified or not. If it is in fact verified, I'd like those results to be displayed at the top of the list. I admit I’m not the most familiar with PHP and I’ve been doing a lot of reading up since I figured there could possibly be a method to make this easier. Reading around it sounded like the uasort() was what I needed for its sorting abilities and that it maintains index association, but I’m a little confused as how to properly use it. The original block of PHP I am working on looks like this:
<?php
if (count($results)):
$lexisIndex = 1;
foreach ($results as $person){
$person->isVerified = (substr($person->first_name, 0, 1) == substr($fname, 0, 1) && $person->last_name == $lname );
$person->lexisIndex = $lexisIndex++;
}
foreach ($results as $person):
?>
From here I put the $results into an array and passed it into a uasort function. After I added what I thought I needed the code looked like this:
<?php
if (count($results)):
$lexisIndex = 1;
foreach ($results as $person){
$person->isVerified = (substr($person->first_name, 0, 1) == substr($fname, 0, 1) && $person->last_name == $lname );
$person->lexisIndex = $lexisIndex++;
}
$array = array($results);
uasort($array, function($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b ? -1 : 1);
});
foreach ($results as $person):
?>
This still didn't work for me and didn't throw any errors so I'm having a heck of a time trying to work this one out. Am I on the right track with what I'm using? Or can this be done in a more efficient manner? I apologize if this is sounds like a question that has been asked before, but I couldn't find a fix while searching around. If this is a duplicate I'm more than happy to click a link and do some more reading. Thanks for your time and help.
$a and $b are the elements of the array. So in your case, they are person objects, and that's how you should compare them:
uasort($array, function($a, $b) {
// isVerified is most important. Move to top.
if ($a->isVerified != $b->isVerified ) {
return ($a->verified ? -1 : 1);
}
// If neither or both are verified, compare based on lexisindex.
return $a->lexisIndex - $b->lexisIndex;
});
The last line is just a shortcut. A comparison function doesn't need to return -1, 0 or 1. It just accepts any value < 0 to indicate that $a is 'smaller' than $b, 0 for equality or > 0 for larger. So your current compare callback, which would compare numeric values, could also just be written as return $a - $b;. You could also write the last line like this, but it's not necessary:
return ($a->lexisIndex < $b->lexisIndex ? -1 : 1);

Sort php sub array based on sub key

Here is a snippet of my array that is used in php 5.3.x :
$arr[$category][$item][$attr_qty] = 3;
$arr[$category][$item][$attr_price] = 12.00;
$category are arbitrary integers, as are $item, as are $attr_qty and $attr_price.
Is there a quick way of sorting, by $attr_qty, the items in each category?
Using integers makes the code easier, but I get the feeling I will have to use associative arrays.
You can use usort which allows you to specify a custom sorting function
usort($arr, 'customSortFunction');
function customSortFunction($a, $b)
{
if ($a['item']['attr_qty'] > $b['item']['attr_qty']) return 1; //First element is bigger
elseif ($a['item']['attr_qty'] < $b['item']['attr_qty']) return -1; //Second element is bigger
else return 0; //Both are equal
}

Stable sort array using second dimension key

I have to use a webservice which return JSON. After decode JSON I get array:
$arrays[0]["2013-04-09"]=$someValue;
$arrays[1]["2013-04-09"]=$someValue;
$arrays[2]["2013-04-11"]=$someValue;
$arrays[3]["2013-04-05"]=$someValue;
$arrays[4]["2013-04-09"]=$someValue;
I want sort (stable way and using key of second dim key) array and get as result:
$arrays[3]["2013-04-05"];
$arrays[0]["2013-04-09"]; //stable way don't swap with next val
$arrays[1]["2013-04-09"]; //stable way don't swap with next and prev vel
$arrays[4]["2013-04-09"]; //stable way, don't swap with prev val
$arrays[2]["2013-04-11"];
Can you help me? I try create own function of sort beacause ksort or krsort sort using only first dim key. Thank you for answers.
EDIT:
I try write my own function - and this works - I got wrong "valid answers" in my units test and this is reason that I said that this isn't works:
private function getResult(){
...
usort($arrays,array($this,'mycmp'));
...
}
private function mycmp($a, $b){
foreach($a as $key=>$val){
$first = $key;
}
foreach($b as $key=>$val){
$second = $key;
}
if ($first == $second){
return 0;
}
return ($first < $second) ? -1:1;
}
THANKS FOR HELP
Assuming that each of the sub-arrays has the date key in the same place (e.g. always first key in the object), you could do the following:
$keyPosition = 0; // location of the key in the array structures
usort($data, function ($a, $b) use ($keyPosition) {
$aKeys = array_keys($a);
$bKeys = array_keys($b);
return strcmp($aKeys[$keyPosition], $bKeys[$keyPosition]);
});
However, what you should be aware is that PHP no longer supports stable sorting, so you may not get exactly the same ordering as previous. If this is an absolute requirement, you may need to roll your own sort function. Here's a Gist that might help.

Categories