Sort array with uasort - php

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);

Related

Detect the difference between two arrays using laravel

I have two arrays and I need to verify what is different between them, like:
Something exists in array A but not in B,
One argument is diferent in array A when comparing to B...
$temp = import_temp::select('cod_disciplina', 'cod_turma', 'hr_inicio', 'hr_fim', 'dia_semana')->get();
$turmas;
foreach($temp as $t)
{
$turmas = Horario::select('cod_disciplina', 'cod_turma', 'hr_inicio', 'hr_fim', 'dia_semana')
->whereIn('cod_disciplina', $temp->lists('cod_disciplina'))
->whereIn('cod_turma', $temp->lists('cod_turma'))
->where('ano_semestre', $ano_semestre)->get();
}
When I do:
print_r($turmas->toArray());
print_r($temp->toArray());
I get:
How may I compare these keys and identify when something changed is new or is missing.
Tried working with array_diff_assoc but I get the following error:
Array to string conversion
array_diff_assoc($temp->toArray(), $turmas->toArray());
Also tried This that I found in another answer in a similar question but didn't work.
First define your own function to compare arrays:
function arrayCmp($a, $b) {
if ($a < $b) {
return -1;
} elseif ($a > $b) {
return 1;
} else {
return 0;
}
}
Then if you want elements that are present in $a bot not in $b then you call:
$diff = array_udiff($a, $b, 'arrayCmp');

PHP array intersect for only one occurrence of value

I'm trying to create a function to find the HCF of two values. I currently have a function that finds all the prime factors of each value and returns them in an array. To find the HCF, all that has to be done would be to compare the similar values in each array then multiply them together.
My code currently looks like this:
function hcf($x, $y) {
$hcf = array_product(array_intersect(prm_fac($x), prm_fac($y)));
if ($hcf != 0)
return $hcf;
else
return 1;
It's hard to explain, so I will show an example of the problem: If I try and find the HCF of 10 and 8, the prime factors of 10 will be 2, 5; the prime factors of 8 will be 2, 2, 2. The similar values in both arrays will be 2.
However, when I use the array_intersect function, it takes all the occurrences of 2, instead of just the single occurrence where it intersects. So instead of getting 2, I will get 2, 2, 2. How can I fix this problem?
Here is another example: I need to find the HCF of 4 and 16. The prime factors of 4 are 2, 2; the prime factors of 16 are 2, 2, 2, 2. I need to find the which values are the same for both arrays. If I use array_intersect on both arrays, it will give me 2, 2, 2, 2 instead of 2, 2. How do I fix this?
Here is the prm_fac function:
function prm_fac($n) {
$factors = array();
while ($n % 2 == 0) {
$factors[] = 2;
$n /= 2;
}
for ($i = 3; $i <= sqrt($n); $i += 2) {
while ($n % $i == 0) {
$factors[] = $i;
$n /= $i;
}
}
if ($n != 1)
$factors[] = $n;
return $factors;
}
Instead of array_intersect you could use this custom function instead, which will take into account that values can repeat, but will only take them when they repeat as many times in both arrays.
The rest of your code can stay:
function common_values($a, $b) {
return array_filter($a, function($v) use (&$b) {
return ($i = array_search($v, $b)) !== false && ($b[$i] = -1);
});
}
So, call it like this:
function hcf($x, $y) {
$hcf = array_product(common_values(prm_fac($x), prm_fac($y)));
if ($hcf != 0)
return $hcf;
else
return 1;
}
Explanation of the function
array_filter($a, ...) iterates over every element of $a, and for each of them calls the function provided in the second argument. If that function returns a truthy value, the corresponding element will be included (and only then) in the array that is returned by array_filter.
That inner return value is calculated as follows:
($i = array_search($v, $b)) finds the index where the value ($v) from $a occurs in $b. This index is assigned to the variable $i (on-the-fly). Then its value is compared with false, which tells us whether there was a match or not. If not, the rest of the expression is not evaluated because the && can never make the total expression true again. And so the return value is false, i.e. this value from $a is excluded (because it does not occur in $b).
In the other case, $i will not be false but an integer index, so the first comparison is true. Then the next part of the && is evaluated:
($b[$i] = -1)
The matching value in $b is wiped out so to make sure it cannot match again in any next iteration. It is wiped out with a negative value, as factors are expected to be always positive, and non-zero values also are truthy so that the return value of array_filter is true, i.e. this value from $a must be included in the result.
Notes and refereces
Note that HCF is also known as GCD. See also this solution to get it in a more direct way, or use gmp-gcd from the GMP extension.
You can use array_unique() to remove duplicates from the result array returned from array_intersect().
I think it would be better if you remove duplicates from the prm_fac() array. Something like :
$hcf = array_product(array_intersect(array_unique(prm_fac($x)), array_unique(prm_fac($y))));
Best practice would be to write it in you prm_fac function itself -
function prm_fac($val) {
.
.
.
return array_unique($factors);
}
We can make use of foreach to get the actual product array - This works for the egs I tried.
$product = array();
$array1 = prm_fac($x); //return the unique $x values
$array2 = prm_fac($y); //return the unique $y values
foreach ($array1 as $val1) {
foreach ($array2 as $val2) {
// Form the product array if the iterated values are present in the other array
if (in_array($val2, $array1) && in_array($val1, $array2)) {
$product[] = $val1;
$product[] = $val2;
}
}
}
Finally,
$hcf = array_product($product); //should give the proper product of values.

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

size of an array

is there a way of finding the sizeof an array without using sizeof($array) and / or count($array)?
If you want to know the number of items in an array, you have two solutions :
Using the count() function -- that's the best idea
Looping over all items, incrementing a counter -- that's a bad idea.
For an example using the second idea :
$num = 0;
foreach ($array as $item) {
$num++;
}
echo "Num of items : $num";
But, again : bad idea !
Edit : just for fun, here's another example of looping over the array, but, this time, using array_map() and an anonymous function (requires PHP >= 5.3) :
$array = array(1, 2, 3, 4, 5);
$count = 0;
array_map(function ($item) use (& $count) {
$count++;
}, $array);
echo "Num of items : $count";
Here, too, bad idea -- even if fun ^^
You could use foreach and manually count the number of elements in the array, but I don't see why you would want to since this will provide no advantage over using either the sizeof or count functions.
Even though there is no point doing a foreach or anything else for that matter... what about array_reduce:
array_reduce($array, function($count, $element) {
return $count + 1;
}, 0);
Just for something different :D

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

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.

Categories