PHP array intersect for only one occurrence of value - php

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.

Related

Find matching value or round up in case number is higher then closest value

I'm trying to implement function where user enters a number to the application, for example 101.
Based on this number, function should loop through an array and find matching value. If not found, it should find closest values and round up to higher one. I'm not sure how to explain this properly, but here is an example:
Array consists numbers 100, 150, 200.
• User enters number 101. No exact number was found, function should return value 150.
• User enters number 151. No exact number was found, function should return value 200.
• User enters number 150. Exact number was found, function should return value 150.
I've made a function which finds matching or closest value, but this isn't quite what I want, because it doesn't round the number up, in case exact match wasn't found.
$arr(100, 150, 200)
$search = 101;
function getClosest($search, $arr) {
$closest = null;
foreach ($arr as $item) {
if ($closest === null || abs($search - $closest) > abs($item - $search)) {
$closest = $item;
}
}
return $closest;
}
In this case it returns the closest number to 101, which is 100. I would like it to return 150. How can I fix this?
You can do something like this. Sort the array first so you can get the next highest value.
<?php
$array = array(100,150,200);
$number = 101;
echo closest($array, $number);
function closest($array, $number) {
sort($array);
foreach ($array as $a) {
if ($a >= $number) return $a;
}
return end($array); // or return NULL;
}
?>

Find index if values in single array on either side are equal when summed

I have a whiteboard question that I think is way beyond my skill and so don't even know how to approach this.
I want to iterate through each value and sum the elements on left/right side and if they're equal return the index value.
So:
[1, 2, 3, 4, 3, 2, 1]; // return 3
The official question:
You are going to be given an array of integers. Your job is to take
that array and find an index N where the sum of the integers to the
left of N is equal to the sum of the integers to the right of N. If
there is no index that would make this happen, return -1.
Is anyone kind enough to help me out? I've looked at array_map() and array_filter() and while helpful I can't think of how to traverse back and forth between the current index when iterating the array.
This can be done with a simple for loop over the full range of an array combined with array_slice and array_sum.
function doSomething(array $data): int {
for ($i = 0, $count = count($data); $i < $count; $i++) {
$left = $i > 0 ? array_slice($data, 0, $i) : [ $data[0] ];
$right = $i > 0 ? array_slice($data, $i + 1) : $data;
$left_result = array_sum($left);
$right_result = array_sum($right);
if ($left_result === $right_result) {
return $i;
}
}
return -1;
}
This small piece of code loops over the whole array and sums up the left and the right of the current position of the array. The results will be compared and if the results are the same, the key of the array will be returned.
For huge arrays you can try to reduce memory consumption by using a yield or an Iterator instance.

Find two values that are equal in array

$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>";
}
}

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

check if two different values exists in a php array

Let's say I have a php array that could be all 1s, all 2s or all 1s and 2s. For example I could have array(1, 1, 1, 1, 1, 1), or array(2, 2, 2, 2, 2, 2) or array(2, 2, 1, 1, 2, 1).
How do I check if my array actually is an array of all 1s, all 2s or if my array actually contains both 1s and 2s?
In case you wanted to know for PHP, you can use array_unique() to probe which distinct values exist:
if (count(array_unique($array)) == 1) {
// It's either full of 1s or 0s.
// Then just probe the first entry.
}
You could add all the values in the array together. If they equal the length of the array or they equal 0 they are all 1s or all 0s.
The simplest way is to just count the number of ones and zeroes. For example (in python):
ones = zeroes = 0;
for i in range(len(my_array)):
if my_array[i] == 1: ones = ones + 1
else zeroes = zeroes + 1
You can also multiply each element together (1 if all ones) and add each element in the array (0 if all elements are zero)
In Java ...
public static void allTheSame(int[] array) {
for (int i = 1; i < array.length; i++) {
if (array[i] != array[i - 1]) {
return false;
}
}
return true;
}
This algorithm can be transcribed into the other listed languages, though their may be neater ways to do it in some. (But watch out for the efficiency of the neat solutions ... if that matters to your application.)
Note that this approach will deliver a false result faster than any neat solution that involves collating or summing the array elements, and it makes no assumption about what the element values are.
Note: this answer was written when the tagging indicated that the OP wanted solutions in Java, Javascript and PHP. Check the Question's edit history ...
You can do it with a simple if-statement. Here's some JavaScript:
if (myArray.indexOf(1) > -1) {
// there are 1s, are there 0s?
if (myArray.indexOf(0) > -1) {
console.log("1s and 0!");
} else {
console.log("Only 1s.");
}
} else {
console.log("Only 0s.");
}
Working example: http://jsfiddle.net/daNEH/
Try this code:
int[] intArray = new int[5];
boolean hasZero, hasOne, hasBoth;
for(int integer : intArray)
{
switch(integer)
{
case 0:
hasZero = true;
break;
case 1:
hasOne = true;
break;
}
}
hasBoth = hasZero && hasOne;
function allElementsEqual(array){
var start = array[0],
same = true;
for(i = 1;i < array.length;i++){
same &= (start === array[1]);
}
return same;
}
This function should do the job fine http://jsfiddle.net/WNxg4/
Another way would be to use array_diff, provided you only had two different numbers. Just compare the haystack of numbers to an array with a single number (pick one of the ones in the haystacks).
For example:
$haystack_mixed = array(2,2,2,1,1);
$haystack_1 = array(1,1,1,1);
$haystack_2 = array(2,2,2,2);
print_r(array_diff($haystack_mixed, array(1)));
// The result is not the same as the $haystack since there are 1's in it.
// Array ( [0] => 2 [1] => 2 [2] => 2 )
print_r(array_diff($haystack_1, array(1)));
// This one is made up of all 1's
// Array ( )
print_r(array_diff($haystack_2, array(1)));
// This one is made up of all 2's (same length as $haystack_2)
// Array ( [0] => 2 [1] => 2 [2] => 2 [3] => 2 )
So you can test the length of the resulting array.
I think you could use the array_sum or array_filter functions for this.
I have read the 9 answers and they're all pretty fancy, I think just go with the simplest way.
is_mixed($array){
$count = count($array);
//we go trough every element in the array
for($i=1;$i<$count;$i++){
//if the element n is distinct from the n-1
//then return true (is_mixed)
if ($array[$i] != $array[$i-1]) return true;
}
//if it didn't return anything yet, it means
//all the elements are the same. Then, just
//return the first one, as they're all the same
// (either 1 or 2)
return $array[0];
}
this second one I actually like the most:
function what_they_are($array){
$total = array_sum($array);
$count = count($array);
if ($total == 0) {
return "they're all 0";
}else if ($total/$count == 2){
return "they're all 2";
}else if ($total == $count){
return "they're all 1";
}else{
return "they're mixed";
}
}

Categories