I have an array in PHP with repeating numbers and I would like to find the most frequent but only when there is only one of that.
while (count(array_count_values($arr)) > 1) {
$minim = min(array_count_values($arr));
while ($minim == min(array_count_values($arr))) {
unset($arr[array_search(array_search(min(array_count_values($arr)), array_count_values($idList)), $arr)]);
$arr = array_splice($arr, 0, 1);
}
}
In my code the first while runs until I have only one number (multiple times) in the array and with the second one I delete the less frequent numbers.
My problem is the I get this error for my second min(): "Array must contain at least one element".
I have an array in PHP with repeating numbers and I would like to find the most frequent but only when there is only one of that.
Your approach seems rather complicated.
Here's how I'd do that:
$numbers = [1, 6, 5, 6, 2, 1, 6, 7, 8]; // positive test case
//$numbers = [1, 6, 5, 6, 2, 1, 6, 7, 8, 1]; // negative test case
$count = array_count_values($numbers); // get count of occurrence for each number
arsort($count); // sort by occurrence, descending
$first = key($count); // get key of first element, because that is the/one
// of the highest number(s)
$count_first = current($count); // get occurrence for first array value
$count_second = next($count); // get occurrence for second array value
if($count_first != $count_second) { // did they occur in different frequencies?
echo $first . ' occurred most in input array.';
}
else {
echo 'input array contained multiple values with highest occurrence.';
}
You could probably perform an array_reduce on the counts to find the max, but because array_reduce does not give you access to the iterable's key, you'd have to perform additional transformation.
Instead, I would recommend you build your own MaxHeap by extending from SplMaxHeap
class MaxHeap extends SplMaxHeap {
public function compare($a, $b) {
if (current($a) < current($b))
return -1;
elseif (current($a) > current($b))
return 1;
else
return 0;
}
}
Then we can use it as such – the answer says [ 7 => 4 ] which means: 7 is the most common number, appearing 4 times
$heap = new MaxHeap();
foreach (array_count_values($numbers) as $n => $count)
$heap->insert([$n => $count]);
print_r($heap->top());
// [ 7 => 4 ]
printf("%d is the most common number, appearing %d times",
key($heap->top()),
current($heap->top())
);
// 7 is the most common number, appearing 4 times
complete script
$numbers = [0, 1, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 7, 7, 8, 8, 9];
class MaxHeap extends SplMaxHeap {
public function compare($a, $b) {
if (current($a) < current($b))
return -1;
elseif (current($a) > current($b))
return 1;
else
return 0;
}
}
$heap = new MaxHeap();
foreach (array_count_values($numbers) as $n => $count)
$heap->insert([$n => $count]);
printf("%d is the most common number, appearing %d times",
key($heap->top()),
current($heap->top())
);
revision history
I was unaware of PHP's native array_count_values function. I removed the more complex array_reduce in favor of this super specialised function. Thanks, #CBroe.
Related
I have a simple whiteboard question:
Create a function that takes both a string and an array of numbers as arguments. Rearrange the letters in the string to be in the order
specified by the index numbers. Return the "remixed" string. Examples
remix("abcd", [0, 3, 1, 2]) ➞ "adbc"
I submitted my code but it's not being accepted and I am unable to see why. I wrote:
function remix($str, $arr) {
$strArr = str_split($str);
for($i = 0; $i < count($strArr); $i++) {
$arr[$i] = $strArr[$arr[$i]];
}
return implode("", $arr);
}
remix("computer", [0, 2, 1, 5, 3, 6, 7, 4]); // ➞ "cmourpte"
Can anyone see something wrong with it? It's always regular characters, no special cases FYI. Very confused.
It looks like the assignment part is putting the value the wrong way round (i.e. it should be assigning the char at position $i to $arr[$i])...
function remix($str, $arr) {
$strArr = str_split($str);
for($i = 0; $i < count($strArr); $i++) {
$strArr[$arr[$i]] = $str[$i];
}
return implode("", $strArr);
}
which for
echo remix("PlOt", [1, 3, 0, 2]).PHP_EOL;
echo remix("computer", [0, 2, 1, 5, 3, 6, 7, 4]);
gives
OPtl
cmourpte
Just to check, this passes the test.
Well it would seem you missed that the remix was giving an incorrect result.
But I would have done it this way as by default a PHP String is in fact an array already.
function remix($str, $arr) {
$mixed = '';
foreach ($arr as $i) {
$mixed .= $str[$i];
}
return $mixed;
}
echo remix("computer", [0, 2, 1, 5, 3, 6, 7, 4]);
RESULT
cmotperu
In fact if this is the question, the stated answers are wrong in ALL cases.
Create a function that takes both a string and an array of numbers as arguments.
Rearrange the letters in the string to be in the order specified by the index numbers.
Return the "remixed" string.
Examples
remix("abcd", [0, 3, 1, 2]) ➞ "acdb"
The string you'll be returning will have: "a" at index 0, "b" at index 3, "c" at index 1, "d" at index 2, because the order of those characters maps to their corresponding numbers in the index array.
remix("PlOt", [1, 3, 0, 2]) ➞ "OPtl"
remix("computer", [0, 2, 1, 5, 3, 6, 7, 4]) ➞ "cmourpte"
$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>";
}
}
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;
}
}
This question already has answers here:
easier way to flatten or merge php array than this?
(2 answers)
Sort array by value alphabetically php
(4 answers)
Closed 6 months ago.
I would like to create a sorted array from a variable number of pre-sorted arrays.
Given {A1, ..., An} which are pre-sorted arrays, I would like to create At, which is the combination of {A1, ..., An} and is sorted in the same way.
Example :
Given :
A1 = [2, 4, 9, 16]
A2 = [-3, 4, 98, 116]
...
An = [1, 7, 17, 76, 512]
I would like :
At = [-3, 1, 2, 4, 4, 9, 16, 17, 76, 98, 116, 512]
What it is the most efficient way to compute this array ?
Thanks
It's simple. We have A1..AN - pre-sorted lists and same amount of indexes I1..IN set to zero (for zero-based lists). Now we need to form merged list from this. To do this we need to find smallest (or biggest depends on what's initial sort order) element from all these lists. It's obvious that this is one of the A1[I1] A2[I2] .. AN[IN] element. So we just go through all these elements and choose smallest. Let's say it was element in A2. We put it into our new big list and increment I2. Now we have same situation as in the beginning and should repeat all these steps again or stop if all lists exhausted.
Example:
A1 = [1, 2, 6]
A2 = [2, 4, 5]
A = []
I1 = 0
I2 = 0
-------------
A = [1]
I1 = 1
I2 = 0
-------------
A = [1, 2]
I1 = 2
I2 = 0
-------------
A = [1, 2, 2]
I1 = 2
I2 = 1
...
I have implemented a function doing what I want.
What do you think of the performance ? Do you have any advice to improve it ?
Sorting function:
function sortPreSortedArrays($arrays, $comparisonFunction, $order = 'asc')
{
$sortedArray = array();
/* Sort */
while(sizeof($arrays) !== 0)
{
/* Find the greatest value */
$max = true;
$keyMax = -1;
foreach($arrays as $key => $array)
{
if($max === true || $comparisonFunction(end($array), $max))
{
$max = end($array);
$keyMax = $key;
}
}
/* Take the greatest value */
array_push($sortedArray, array_pop($arrays[$keyMax]));
if(sizeof($arrays[$keyMax]) === 0) unset($arrays[$keyMax]);
}
/* Return */
if($order === 'asc')
return array_reverse($sortedArray);
else
return $sortedArray;
Comparison function:
function compareLogArrayDate($log1, $log2)
{
$t1 = $log1['date'];
$t2 = $log2['date'];
return ($t1 > $t2) ? true : false;
}
Edit: In order to improved the performancse, I have tried do use the most efficient array functions (array_pop O(1) instead of array_shift O(n). Nevertheless I am still using unset. :|
$A1 = [2, 4, 9, 16];
$A2 = [-3, 4, 98, 116];
$An = [1, 7, 17, 76, 512];
// create arrays of ith elements
$return = call_user_func_array('array_map', [null, $A1, $A2, $An]);
// sort arrays
array_walk($return, 'sort');
// create new arrays
$return = call_user_func_array('array_merge', $return);
// remove null values lefted after first operation
$return = array_filter($return, 'is_scalar');
var_dump($return);
This works but, is it reliable? Will it always work since the MAINarray is containing ANOTHERarray which contains 2 values and min should work in MAINarray and find lowest value in its subarays.
$a[0]=array(0=>522,1=>'Truck');
$a[1]=array(0=>230,1=>'Bear');
$a[2]=array(0=>13,1=>'Feather');
$a[3]=array(0=>40,1=>'Rabit');
$z=min($a);$y=max($a);
echo "The easiest is ".$z[1]." with only ".$z[0]." pounds!<br>";
echo "The heaviest is ".$y[1]." with ".$y[0]." pounds!";
What you say?
Yes, it is reliable. It's safe to assume that min(array(1, 2, ..., n)) is equivalent to min(1, 2, ..., n), and the documentation specifically covers how min compares multiple arrays:
// With multiple arrays, min compares from left to right
// so in our example: 2 == 2, but 4 < 5
$val = min(array(2, 4, 8), array(2, 5, 1)); // array(2, 4, 8)
My understanding of how min works with your type of input is:
Only consider the arrays with the fewest number of items.
Compare the first elements
If there is a tie, compare the next element of each array. Repeat step.
e.g.:
array(
array(1, "A"),
array(2), // min
)
array(
array(1, "A"), // min
array(2, "A"),
)
array(
array(1, "Z"),
array(1, "A"), // min
)
I don't have a source for that information, but it's how I remember it working.
I’m not sure if this always works. In case of doubt just implement the function yourself:
function extremes($array, $key=0) {
if (count($array) === 0) return null;
$min = $max = null;
foreach ($array as &$val) {
if (!isset($val[$key])) continue;
if ($min > $val[$key]) $min = $val[$key];
else if ($max < $val[$key]) $max = $val[$key];
}
return array($min, $max);
}
$a = array(array(522, 'Truck'), array(230, 'Bear'), array(13, 'Feather'), array(40, 'Rabit'));
list($z, $y) = extremes($a);
The only evidence I could find to say it was intended to be able to use an array of arrays to compare is this bug report to improve the php docs. http://bugs.php.net/bug.php?id=50607
Although, it's quite unlikely for them to remove this functionality because of the way it has to compare arrays already by going into them recursively. The arguments list itself when using the function normally would be an array because there is no finite number of arguments.