$arr = array(
1, 1, 2, 3, 4
);
How to find out the pair from this array ?
Keep in mind that the pair from array could be any number (1,2,4,3,2) or (3,3,1,2,4); I just give an random example above.
if there is a pair in array
echo "The pair number is 1";
All of my methods will return the desired result as long as there IS a duplicate. It is also assumed because of your sample input, that there is only 1 duplicate in the array. The difference between my methods (and the other answers on this page) will be milliseconds at most for your input size. Because your users will not be able to distinguish between any of the correct methods on this page, I will suggest that the method that you implement should be determined by "readability","simplicity", and/or "brevity". There are many coders who always default to for/foreach/while loops. There are others who always defer to functional iterators. Your choice will probably just come to down to "your coding style".
Input:
$arr=[1,1,2,3,4];
Method #1: array_count_values(), arsort(), key()
$result=array_count_values($arr);
arsort($result); // this doesn't return an array, so cannot be nested
echo key($result);
// if no duplicate, this will return the first value from the input array
Explanation: generate new array of value occurrences, sort new array by occurrences from highest to lowest, return the key.
Method #2: array_count_values(), array_filter(), key()
echo key(array_filter(array_count_values($arr),function($v){return $v!=1;}));
// if no duplicate, this will return null
Explanation: generate the array of value occurrences, filter out the 1's, return the lone key.
Method #3: array_unique(), array_diff_key(), current()
echo current(array_diff_key($arr,array_unique($arr)));
// if no duplicate, this will return false
Explanation: remove duplicates and preserve the keys, find element that went missing, return the lone value.
Further consideration after reading: https://www.exakat.io/avoid-array_unique/ and the accepted answer from array_unique vs array_flip I have a new favorite 2-function one-liner...
Method #4: array_count_values(), array_flip()
echo array_flip(array_count_values($arr))[2];
// if no duplicate, this will show a notice because it is trying to access a non-existent element
// you can use a suppressor '#' like this:
// echo #array_flip(array_count_values($arr))[2];
// this will return null on no duplicate
Explanation: count the occurrences (which makes keys of the values), swap the keys and values (creating a 2-element array), access the 2 key without a function call. Quick-smart!
If you wanted to implement Method #4, you can write something like this:(demo)
$dupe=#array_flip(array_count_values($arr))[2];
if($dupe!==null){
echo "The pair number is $dupe";
}else{
echo "There were no pairs";
}
There will be many ways to achieve your desired result as you can see from all of the answers, but I'll stop here.
First sort your array, then use foreach loop and if current and next are equal and not echo this item before this time, echo item is pair.
$arr = array(
1, 3, 1, 2, 2, 2, 3, 4
);
sort($arr);
$last_number = null;
foreach($arr as $key => $item) {
if(array_key_exists($key + 1, $arr)) {
if($item === $arr[$key + 1]) {
if($last_number !== $item) {//prevent duplicate echo pair of one item
echo 'The pair number is ' . $item;
$last_number = $item;
}
}
}
}
Use built-in array_count_values. Then iterate over results and check if count is greater than 1.
$arr = array(
1, 1, 2, 3, 4
);
$values = array_count_values($arr);
foreach ($values as $key => $value) {
if ($value > 1) {
echo 'Pair: ' . $key;
// break, if you need to show first pair only
// break;
}
}
You can first count all the values in the array, then for each distinct value check if it occurs twice in the array.
<?php
$array = array(1, 1, 2, 3, 4);
$vars = array_count_values($array);
foreach($vars as $key => $var) {
if($var == 2) {
echo "The pair number is " . $key . "<br>";
}
}
?>
First sort the array, then check if there are two numbers on consecutive indexes with the same value.
This is the most efficient solution: sorting is done in O(n log n) time1 and doing the checking takes O(n) time. Overall, you'll have the answer in O(n log n) time. Naive approaches will use O(n2) time.
If you don't want to modify the original array, do all of the above on a copy.
function hasEqualPair($arr) {
sort($arr);
$l = count($arr);
for ($i = 1; $i < $l; ++$i) {
if ($arr[$i] === $arr[$i - 1]) {
return true;
}
}
return false;
}
var_dump(hasEqualPair(array(1, 2, 4, 3, 2))); // true
var_dump(hasEqualPair(array(1, 1, 2, 3, 4))); // true
var_dump(hasEqualPair(array(3, 3, 1, 2, 4))); // true
var_dump(hasEqualPair(array(3, 5, 1, 2, 4))); // false
Try it online!
Of course, if you want to know what the pair is, just change return true in the above function to return $arr[$i]. You then might want to change the function name to getEqualPair as well.
1 The sort function in PHP uses Quicksort. It is possible to make that always run in O(n log n) time, but the implementation in PHP probably has an average running time of O(n log n), with worst case running time still O(n2).
$arr = array(
1, 1, 2, 3, 4
);
echo "<pre>";
$vals = array_count_values($arr);
foreach ($vals as $key => $value) {
if($value%2==0){
echo "The pair number is ".$key."<br>";
}else{
echo "The not pair number is ".$key."<br>";
}
}
Related
I have array with numeric values and I want to get key of first element which has value equal or greater than 5. Is there more elegant way than looping all elements in foreach?
// "dirty" way
foreach ([0, 0, 4, 4, 5, 7] as $key => $value) {
if ($value >= 5) {
echo $key;
break;
}
}
The algorithm itself is perfectly fine, don't touch it.
That said, you could add some ribbons by writing a generic search function:
// find first key (from beginning of $a) for which the corresponding
// array element satisfies predicate $fn
function array_find(array $a, callable $fn)
{
foreach ($a as $key => $value) {
if ($fn($value, $key, $a)) {
return $key;
}
}
return false;
}
$key = array_find([0, 0, 4, 4, 5, 7], function($value) {
return $value >= 5;
});
Now, although this is a more elegant approach, it's less efficient; there's a considerable overhead of calling the closure at each item. If performance is paramount, use what you have and run with it.
Using array_search() can be efficient when seeking the earliest occurring match, but it is inappropriate in this case because it will not allow you to supply your required logic to the search.
Using functional iterators like array_map() and array_filter() are not ideal because they lack the ability to "short circuit" as soon as a match is made. In my own professional projects, I would not use a functional-style technique -- even on a relatively small data set -- because there is no valuable gain in doing so.
Considering the above, just use a classic loop with a condition in its body which breaks the loop as soon as a qualifying match occurs.
The $test variable is defined within a loop of test cases in my runnable demo links. To trial my code below, be sure to define $test as your needle before entering the loop.
Earliest key where array value is greater than or equal to the needle: (Demo)
$array = [0, 0, 4, 4, 5, 7];
$foundKey = 'not found';
foreach ($array as $key => $value) {
if ($value >= $test) {
$foundKey = $key;
break;
}
}
Get key of highest value not greater than the needle: (Demo)
$array = [0, 0, 4, 4, 5, 7];
$foundKey = 'not found';
foreach ($array as $key => $value) {
if ($value > $test) {
break;
}
$foundKey = $key;
}
How to quickly remove elements in an array that are < 5 apart from each other quickly.
example:
array(1, 3, 5, 8, 11, 15);
needs to return the following cause they are more than 5 if you calculate the difference:
array(1, 8, 15);
This seems like it should be a built-in function in php for this. But I'm baffled.
There's nothing built-in for this, but it's a pretty easy thing to accomplish.
First, sort your array, unless it's already sorted.
sort($your_array);
Initialize your result array with the first element, and then iterate the array. Each time you get to a value at least 5 greater than the previous value, add it to the result and reset the previous value to that value.
$result[] = $previous = reset($your_array);
foreach ($your_array as $value) {
if ($value - $previous >= 5) {
$result[] = $previous = $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.
Perhaps this has been asked several times but I can't find the right answer so here goes.
I have two arrays: one with ~135732 and the other one with ~135730 elements. I need to find which items are on the first but not on the second and viceverse and don't know is there is an easy way to achieve that.
This is what I would do it:
$countArr1 = count($arr1);
$countArr2 = count($arr2);
for($i=0; $i < $countArr1; $i++) {
// Check whether current element on $arr1 is on $arr2 or not
if (!in_array($arr1[$i], $arr2)) {
// if it doesn't then add it to $newArr
$newArr[] = $arr1[$i];
}
}
Then I would do the same but inverse for $arr2. In huge arrays could take a while and also could kill memory or server resources, even if it's executed from CLI so which is the best and the most efficient, regarding use of resources, way to achieve this?
EDIT
Let's clear this a bit. I get $arr1 from DB and $arr2 comes from other place. So the big idea is to find which items needs to be updated and which ones needs to be added also which ones needs to be marked as obsolete. In less and common words:
if element is on $arr1 but doesn't exists on $arr2 should be marked as obsolete
if element comes in $arr2 btu doesn't exists on $arr1 then needs to be added (created)
otherwise that element just need to be updated
Clear enough? Feel free to ask everything in order to help on this
EDIT 2
Based on #dakkaron answer I made this code:
// $arr1 and $arr2 are previously built
$sortArr1 = asort($arr1);
$sortArr2 = asort($arr2);
$countArr1 = count($sortArr1);
$countArr2 = count($sortArr2);
$i = $j = 0;
$updArr = $inactiveArr = $newArr = [];
echo "original arr1 count: ", count($arr1), "\n";
echo "original arr2 count: ", count($arr2), "\n";
echo "arr1 count: ", $countArr1, "\n";
echo "arr2 count: ", $countArr2, "\n";
while ( $i < $countArr1 && $j < $countArr2) {
if ($sortArr1[$i] == $sortArr2[$j]) {
//Handle equal values
$updArr[] = $sortArr1[$i];
$i++; $j++;
} else if ($sortArr1[$i] < $sortArr2[$j]) {
//Handle values that are in arr1 but not in arr2
$inactiveArr[] = $sortArr1[$i];
$i++;
} else {
//Handle values that are in arr2 but not in arr1
$newArr[] = $sortArr2[$j];
$j++;
}
}
echo "items update: ", count($updArr), "\n", "items inactive: ", count($inactiveArr), "\n", "items new: ", count($newArr), "\n";
And I got this output:
original arr1 count: 135732
original arr2 count: 135730
arr1 count: 1
arr2 count: 1
items update: 1
items inactive: 0
items new: 0
Why sort count returns 1?
You could take avantage of array_diff: http://php.net/manual/en/function.array-diff.php
Edit
A php function construct is more likely to perform better than an equivalent user-defined one. Searching I found this, but the size of your array is way smaller, and in the end I believe you should benchmark a prototype script with candidate solutions.
See my last comment.
The best solution I can think of would be to first sort both arrays and then compare them from the bottom up.
Start with the lowest element in both arrays and compare them.
If they are equal, take them and move up one element on both arrays.
If they are different, move up one element on the array with the lower value.
If you reached the end of one of the arrays you are done.
After the sorting this should take about O(n) complexity.
This is a bit of code in pseudocode:
arr1 = ...
arr2 = ...
arr1.sort();
arr2.sort();
i1 = 0;
i2 = 0;
while (i1<arr1.length() && i2<arr2.length()) {
if (arr1[i1]==arr2[i2]) {
//Handle equal values
i1++; i2++;
} else if (arr1[i1]<arr2[i2]) {
//Handle values that are in arr1 but not in arr2
i1++;
} else {
//Handle values that are in arr2 but not in arr1
i2++;
}
}
Other than that, if you don't want to implement it yourself, just use array_diff
The best solution i can think of is to sort the second array, and try to look for values from the first array using binary search,
this would take O(nLog(n)) complexity
Since your values are strings, you could take the advantage of PHP’s implementation of arrays using a hash-table internally with O(1) for key lookups:
$diff = [];
// A \ B
$lookup = array_flip($b); // O(n)
foreach ($a as $value) { // O(n)
if (!isset($lookup[$value])) $diff[] = $value;
}
// B \ A
$lookup = array_flip($a); // O(n)
foreach ($b as $value) { // O(n)
if (!isset($lookup[$value])) $diff[] = $value;
}
So in total, it’s O(n) in both space and time.
Of course, in the end you should benchmark it to see if it’s actually more efficient than other solutions here.
Fill hashtable-based dictionary/map (don't know how it is called in PHP) with the second array elements, and check whether every element of the first array presents in this dictionary.
Usual complexity O(N)
for A in arr2
map.insert(A)
for B in arr1
if not map.contains(B) then
element B is on $arr1 but doesn't exists on $arr2
note that this approach doesn't address all problems in your edited question
I have array with numeric values and I want to get key of first element which has value equal or greater than 5. Is there more elegant way than looping all elements in foreach?
// "dirty" way
foreach ([0, 0, 4, 4, 5, 7] as $key => $value) {
if ($value >= 5) {
echo $key;
break;
}
}
The algorithm itself is perfectly fine, don't touch it.
That said, you could add some ribbons by writing a generic search function:
// find first key (from beginning of $a) for which the corresponding
// array element satisfies predicate $fn
function array_find(array $a, callable $fn)
{
foreach ($a as $key => $value) {
if ($fn($value, $key, $a)) {
return $key;
}
}
return false;
}
$key = array_find([0, 0, 4, 4, 5, 7], function($value) {
return $value >= 5;
});
Now, although this is a more elegant approach, it's less efficient; there's a considerable overhead of calling the closure at each item. If performance is paramount, use what you have and run with it.
Using array_search() can be efficient when seeking the earliest occurring match, but it is inappropriate in this case because it will not allow you to supply your required logic to the search.
Using functional iterators like array_map() and array_filter() are not ideal because they lack the ability to "short circuit" as soon as a match is made. In my own professional projects, I would not use a functional-style technique -- even on a relatively small data set -- because there is no valuable gain in doing so.
Considering the above, just use a classic loop with a condition in its body which breaks the loop as soon as a qualifying match occurs.
The $test variable is defined within a loop of test cases in my runnable demo links. To trial my code below, be sure to define $test as your needle before entering the loop.
Earliest key where array value is greater than or equal to the needle: (Demo)
$array = [0, 0, 4, 4, 5, 7];
$foundKey = 'not found';
foreach ($array as $key => $value) {
if ($value >= $test) {
$foundKey = $key;
break;
}
}
Get key of highest value not greater than the needle: (Demo)
$array = [0, 0, 4, 4, 5, 7];
$foundKey = 'not found';
foreach ($array as $key => $value) {
if ($value > $test) {
break;
}
$foundKey = $key;
}