Sort array by both numeric and string keys - php

i have tried both ksort and asort but in both cases all always shows at bottom..
but i want to display array of index 'all' at top then numeric fields should be displayed.
Actually i am adding that key manually.
$result['all'] = new stdClass();
$result['all']->DisciplinaryAction = 'All';
$result['all']->DisciplinaryActionID = 0;
i tried ksort($result) and also tried asort($result) but in both cases text/string always arranged to bottom..
Array
(
[0] => stdClass Object
(
[DisciplinaryAction] => counseling
[DisciplinaryActionID] => 1
)
[1] => stdClass Object
(
[DisciplinaryAction] => verbal warning
[DisciplinaryActionID] => 2
)
[2] => stdClass Object
(
[DisciplinaryAction] => written warning
[DisciplinaryActionID] => 3
)
[3] => stdClass Object
(
[DisciplinaryAction] => suspension
[DisciplinaryActionID] => 4
)
[4] => stdClass Object
(
[DisciplinaryAction] => termination
[DisciplinaryActionID] => 5
)
[all] => stdClass Object
(
[DisciplinaryAction] => All
[DisciplinaryActionID] => 0
)
)

See How can I sort arrays and data in PHP?.
The much more sensible way to do this, rather than sorting, is probably just to add the key to the beginning of the array directly:
$arr = array_merge(array('all' => new stdClass), $arr);

You can use uasort, uksort or usort to define how this should be handled.
http://php.net/manual/en/array.sorting.php
EDIT : Here's a comparison function for uksort
function cmp($a, $b)
{
if ($a == $b)
return 0;
else if (is_string($a))
return -1;
else if (is_string($b))
return 1;
else
return ($a < $b) ? -1 : 1;
}

Related

Sorting CSV Data By A Column With PHP

I've looked on Stack Overflow with no solution that seems to work. Know that I'm a newbie!
Suppose I have a file (data.csv) with the following contents:
year,total
1990,57.58
2011,73.28
1880,54.67
1996,53.41
1950,53.22
1979,52.76
1876,52.62
1883,52.35
1882,52.12
2018,52.23
...then import the data with PHP:
$csv = array_map('str_getcsv', file('data.csv'));
How would I sort both the year and total by the total column in ascending order (such that 1882/52.12 are under $csv[0] in the array and 2011/73.28 are under $csv[10]?
The following and a print($csv); does not seem to be getting the items in the right order:
function compare($a, $b) {
return ($b[0][1] - $a[0][1]);
}
usort($csv, "compare");
Do I need to use typecasting? Thank you!
This will do the trick:
You have to modify the function like this:
function compare($a, $b)
{
// here, comparing "total" column of each row:
return $a[1] >= $b[1] ? 1 : -1;
}
And the new ordering will be:
Array
(
[0] => Array
(
[0] => 1882
[1] => 52.12
)
[1] => Array
(
[0] => 2018
[1] => 52.23
)
[2] => Array
(
[0] => 1883
[1] => 52.35
)
[3] => Array
(
[0] => 1876
[1] => 52.62
)
[4] => Array
(
[0] => 1979
[1] => 52.76
)
[5] => Array
(
[0] => 1950
[1] => 53.22
)
[6] => Array
(
[0] => 1996
[1] => 53.41
)
[7] => Array
(
[0] => 1880
[1] => 54.67
)
[8] => Array
(
[0] => 1990
[1] => 57.58
)
[9] => Array
(
[0] => 2011
[1] => 73.28
)
)
Three problems with your comparison function.
The items that will be compared in your comparison function are going to be arrays corresponding to rows from your CSV file. For example, $a and $b will be things like [1990,57.58] and [1950,53.22].
When you refer to those items in your comparison function, you're looking at index [0][1], but that doesn't exist; the arrays don't have that second dimension. $a[0][1] and $b[0][1] will both be null, so no sorting happens. (You might think you'd get some kind of warning or notice for trying to refer to an int or float with an array index, but you won't, just one of the weird things about PHP.)
You want to sort in ascending order, but putting $b first will sort in descending order.
The comparison function should return an int greater than, less than, or equal to zero depending on the result of the comparison, but yours will return a float. There are various way to make it return an int. The other answer shows how to do it with a ternary expression. If you're using PHP 7, you can use the combined comparison operator, like this:
function compare($a, $b) {
return $a[1] <=> $b[1];
}
Also, you don't need to define a named comparison function, you can use an anonymous function for the second argument if you like.
usort($csv, function($a, $b) {
return $a[1] <=> $b[1];
});

PHP and prevent uasort function from sorting if values are equivalent

I'm using an array with PHP's uasort function. If $a[0]['date'] == $b[0]['date'], then I do not want to sort.
Anyway, my code will sort anyway. How could I prevent it from doing so?
Here is my array:
And here is the array:
Array (
[2764] => Array (
[status] => 0
[0] => Array (
[date] => 2000-01-01
)
[1] => Array (
[date] => 2016-01-16
)
)
[5974] => Array (
[status] => 0
[0] => Array (
[date] => 2000-01-01
)
[1] => Array (
[date] => 2010-12-13
)
)
)
And here is how I'm trying to sort:
uasort($arr, function($a, $b)
{
if ($a['status'] == $b['status'])
{
if ($a[0]['date'] == $b[0]['date'])
{
return 0; # I do not want to sort here, but this will sort anyway - why is that so?
}
else
{
return strcmp($a[0]['date'], $b[0]['date']);
}
}
else
{
return $b['status'] - $a['status'];
}
});
How could I sort by indexes if dates are same? I mean sorting by indexes 2764 and 5974.
As in documentation:
If two members compare as equal, their relative order in the sorted array is undefined.
So you have to introduce extra value, such as original_order and sort by it equal entries.

Intersect (inner join) two arrays with different key names

I have following two multidimensional arrays:
First array:
$array_1_data = Array (
[0] => Array ( [id] => 1 [name] => IT [slug] => it )
[1] => Array ( [id] => 2 [name] => Accounting [slug] => accounting )
)
Second array:
$array_2_data = Array (
[0] => Array ( [cid] => 3 [jid] => 24061 )
[1] => Array ( [cid] => 1 [jid] => 24062 )
)
Expected result:
$some_array = Array (
[0] => Array ( [id] => 1 [name] => IT [slug] => it )
)
I won't mind having [cid] in the result.
I want to intersect these two arrays by [id] of the first array and [cid] of the second array, like inner join in MySQL. I have basic foreach and if else logic for this purpose but speed is a priority now so I'm looking for non-looped solution. For better understanding here is the basic looped solution:
foreach ($array_1_data as $array_1_row ) {
foreach ($array_2_data as $array_2_row ) {
if ($array_2_row['cid'] == $array_1_row['id']) {
//intersection iteration
}
}
}
I tried array_uintersection as follows:
array_uintersect($array_1_data, $array_2_data, function($a1, $a2){
$diff1 = strcasecmp($a1['id'], $a2['cid']);
if ($diff1 != 0) return $diff1;
return 0;
});
But it gives me undefined index 'id'. I checked this question: Comparing two arrays with different key names. First answer for this question gives a looped solution which I want to avoid. Second answer suggests changing SQL structure but I have no control over that. So,
Is there really a non-looped fast solution to this kind of situation?
The solution using array_uintersect_uassoc function:
$result = array_uintersect_uassoc($array_1_data, $array_2_data, function($a, $b){
return strcasecmp($a['id'], $b['cid']);
}, function($a, $b){
return (int) [$a, $b] == ['id', 'cid'];
});
print_r($result);
The output:
Array
(
[0] => Array
(
[id] => 1
[name] => IT
[slug] => it
)
)
According yo your condition: to intersect these two arrays by [id] of the first array and [cid] of the second array, we should consider a key comparison function for those keys only ['id', 'cid'].
Having the needed keys on each comparison step it only remain to compare their values(with value compare function)
http://php.net/manual/en/function.array-uintersect-uassoc.php
DEMO link

Sort an array of objects by deeply nested property selected from an adjacent value

I'm making a call to a REST API and it returns an array of objects. Some of them contain further arrays with objects inside, as in:
Array
(
[0] => stdClass Object
(
[OPPORTUNITY_ID] => 7443729
[CUSTOMFIELDS] => Array
(
[0] => stdClass Object
(
[CUSTOM_FIELD_ID] => OPPORTUNITY_FIELD_7
[FIELD_VALUE] => Zorem
)
[1] => stdClass Object
(
[CUSTOM_FIELD_ID] => OPPORTUNITY_FIELD_8
[FIELD_VALUE] => Zappem
)
)
)
[1] => stdClass Object
(
[OPPORTUNITY_ID] => 7401996
[CUSTOMFIELDS] => Array
(
[0] => stdClass Object
(
[CUSTOM_FIELD_ID] => OPPORTUNITY_FIELD_7
[FIELD_VALUE] => Aorem
)
[1] => stdClass Object
(
[CUSTOM_FIELD_ID] => OPPORTUNITY_FIELD_8
[FIELD_VALUE] => Arappem
)
)
)
// [etc.]
)
What I would like to do is sort the first-level objects in the array based on either OPPORTUNITY_ID or on CUSTOM_FIELD_ID / FIELD_VALUE.
As a scenario, a user would click on the link: "Sort by OPPORTUNITY_FIELD_7" and the array element [1] would become [0] because "Aorem" has a higher alphabetical value than "Zorem".
I've managed to sort the first level objects using usort:
function sort_results( $a, $b ) {
if ($a->OPPORTUNITY_ID == $b->OPPORTUNITY_ID) return 0;
else if ($a->OPPORTUNITY_ID > $b->OPPORTUNITY_ID) return -1;
else return 1;
}
usort( $json_opportunities, "sort_results" );
But I don't know how to get off first base on sorting by level 2 values like OPPORTUNITY_FIELD_7. Any suggestions? Do I need to push everything into an array and sort that way, or is there a way to sort without rewriting the array first?
We just need a more advanced callback for usort(). For example, sorting the array using OPPORTUNITY_FIELD_7 would compare these values:
$restArray[0]->CUSTOMFIELDS[0]->FIELD_VALUE
$restArray[1]->CUSTOMFIELDS[0]->FIELD_VALUE
Because OPPORTUNITY_FIELD_7 is a value in the same array as the value we use to sort rather than being its index, we need to first find the numeric index to which it belongs. This is done by looping through CUSTOMFIELDS until we find OPPORTUNITY_FIELD_7.
// callback for usort() using "OPPORTUNITY_FIELD_7"
function sort_OF7($a, $b) {
// get the index where "OPPORTUNITY_FIELD_7" is stored
foreach($a->CUSTOMFIELDS as $index => $arr) {
if($arr->CUSTOM_FIELD_ID == 'OPPORTUNITY_FIELD_7') {
$ai = $index;
break;
}
}
// again
foreach($b->CUSTOMFIELDS as $index => $arr) {
if($arr->CUSTOM_FIELD_ID == 'OPPORTUNITY_FIELD_7') {
$bi = $index;
break;
}
}
// compare values
return strcmp(
$a->CUSTOMFIELDS[$ai]->FIELD_VALUE,
$b->CUSTOMFIELDS[$bi]->FIELD_VALUE
);
}
This assumes OPPORTUNITY_FIELD_7 is always available in the objects.
We can quickly test it:
// test
$main = JSON_decode('[{"OPPORTUNITY_ID":7443729,"CUSTOMFIELDS":[{"CUSTOM_FIELD_ID":"OPPORTUNITY_FIELD_7","FIELD_VALUE":"Zorem"},{"CUSTOM_FIELD_ID":"OPPORTUNITY_FIELD_8","FIELD_VALUE":"Zappem"}]},{"OPPORTUNITY_ID":7401996,"CUSTOMFIELDS":[{"CUSTOM_FIELD_ID":"OPPORTUNITY_FIELD_7","FIELD_VALUE":"Aorem"},{"CUSTOM_FIELD_ID":"OPPORTUNITY_FIELD_8","FIELD_VALUE":"Arappem"}]}]');
print_r($main);
usort($main, 'sort_OF7');
print_r($main);

Array Intersect giving wrong output

I need to find common elements between two arrays. My code is:
$sql="SELECT DISTINCT fk_paytbl_discounts_discountid as discountid from paytbl_discounts_students WHERE fk_vtiger_cf_601='".$categoryid."'";
$discountstudentinfo=$objdb->customQuery($sql,false);
$sql1="SELECT DISTINCT fk_paytbl_discounts_discountid as discountid from paytbl_discounts_variants WHERE fk_vtiger_products_productid='".$variantid."'";
$discountvariantinfo=$objdb->customQuery($sql1,false);
$commondiscount=array_intersect($discountvariantinfo,$discountstudentinfo);
First array
Array
(
[0] => Array
(
[discountid] => 2
)
[1] => Array
(
[discountid] => 8
)
[2] => Array
(
[discountid] => 5
)
[3] => Array
(
[discountid] => 4
)
)
Second array
Array
(
[0] => Array
(
[discountid] => 1
)
[1] => Array
(
[discountid] => 5
)
)
Common array
Array
(
[0] => Array
(
[discountid] => 1
)
[1] => Array
(
[discountid] => 5
)
)
Common array should have only discountid 5 but its showing 1 also.
Please help me on this
Thanks
http://php.net/array_intersect
Note: Two elements are considered
equal if and only if (string) $elem1
=== (string) $elem2. In words: when the string representation is the same.
So the reason you are experiencing a problem is because, for example:
(string) array('discountid' => 5) == (string) array('discountid' => 8)
If you are running PHP 5.3, this is one solution:
$comparisonFunction = function($elem1, $elem2) {
return $elem1['discountid'] == $elem2['discountid'];
}
$commondiscount = array_uintersect(
$discountvariantinfo,
$discountstudentinfo,
$comparisonFunction
);
Prior to PHP 5.3 you could use the uglier create_function() instead of the nifty closure. If your executing inside a method it would likely be easy to tack on a new private method to use as a callback.
If you are not using PHP 5.3 and you really don't want to use a callback, you could use the following idea:
$uniqueDiscounts = array();
foreach ($discountvariantinfo as $dvi) {
$uniqueDiscounts[$dvi['discountid']] += 1;
}
foreach ($discountstudentinfo as $dsi) {
$uniqueDiscounts[$dsi['discountid']] += 1;
}
$commondiscount = array();
foreach ($uniqueDiscounts as $ud => $count) {
if ($count == 2) {
$commondiscount[] = array('discountid' => $ud);
}
}
You will, of course, want to tidy this up or add comments to explain the algorithm.

Categories