Related
I'm sure this is an extremely obvious question, and that there's a function that does exactly this, but I can't seem to find it. In PHP, I'd like to know if my array has duplicates in it, as efficiently as possible. I don't want to remove them like array_unique does, and I don't particularly want to run array_unique and compare it to the original array to see if they're the same, as this seems very inefficient. As far as performance is concerned, the "expected condition" is that the array has no duplicates.
I'd just like to be able to do something like
if (no_dupes($array))
// this deals with arrays without duplicates
else
// this deals with arrays with duplicates
Is there any obvious function I'm not thinking of?
How to detect duplicate values in PHP array?
has the right title, and is a very similar question, however if you actually read the question, he's looking for array_count_values.
I know you are not after array_unique(). However, you will not find a magical obvious function nor will writing one be faster than making use of the native functions.
I propose:
function array_has_dupes($array) {
// streamline per #Felix
return count($array) !== count(array_unique($array));
}
Adjust the second parameter of array_unique() to meet your comparison needs.
Performance-Optimized Solution
If you care about performance and micro-optimizations, check this one-liner:
function no_dupes(array $input_array) {
return count($input_array) === count(array_flip($input_array));
}
Description:
Function compares number of array elements in $input_array with array_flip'ed elements. Values become keys and guess what - keys must be unique in associative arrays so not unique values are lost and final number of elements is lower than original.
Warning:
As noted in the manual, array keys can be only type of int or string so this is what you must have in original array values to compare, otherwise PHP will start casting with unexpected results. See https://3v4l.org/7bRXI for an example of this fringe-case failure mode.
Proof for an array with 10 million records:
The top-voted solution by Jason McCreary: 14.187316179276s ππππππππππππππ
The accepted solution by Mike Sherov: 2.0736091136932s ππ
This answer's solution: 0.14155888557434s π/10
Test case:
<?php
$elements = array_merge(range(1,10000000),[1]);
$time = microtime(true);
accepted_solution($elements);
echo 'Accepted solution: ', (microtime(true) - $time), 's', PHP_EOL;
$time = microtime(true);
most_voted_solution($elements);
echo 'Most voted solution: ', (microtime(true) - $time), 's', PHP_EOL;
$time = microtime(true);
this_answer_solution($elements);
echo 'This answer solution: ', (microtime(true) - $time), 's', PHP_EOL;
function accepted_solution($array){
$dupe_array = array();
foreach($array as $val){
// sorry, but I had to add below line to remove millions of notices
if(!isset($dupe_array[$val])){$dupe_array[$val]=0;}
if(++$dupe_array[$val] > 1){
return true;
}
}
return false;
}
function most_voted_solution($array) {
return count($array) !== count(array_unique($array));
}
function this_answer_solution(array $input_array) {
return count($input_array) === count(array_flip($input_array));
}
Notice that accepted solution might be faster in certain condition when not unique values are near the beginning of huge array.
You can do:
function has_dupes($array) {
$dupe_array = array();
foreach ($array as $val) {
if (++$dupe_array[$val] > 1) {
return true;
}
}
return false;
}
$hasDuplicates = count($array) > count(array_unique($array));
Will be true if duplicates, or false if no duplicates.
Here's my take on this⦠after some benchmarking, I found this to be the fastest method for this.
function has_duplicates( $array ) {
return count( array_keys( array_flip( $array ) ) ) !== count( $array );
}
β¦or depending on circumstances this could be marginally faster.
function has_duplicates( $array ) {
$array = array_count_values( $array );
rsort( $array );
return $array[0] > 1;
}
$duplicate = false;
if(count(array) != count(array_unique(array))){
$duplicate = true;
}
Keep it simple, silly! ;)
Simple OR logic...
function checkDuplicatesInArray($array){
$duplicates=FALSE;
foreach($array as $k=>$i){
if(!isset($value_{$i})){
$value_{$i}=TRUE;
}
else{
$duplicates|=TRUE;
}
}
return ($duplicates);
}
Regards!
To remove all the empty values from the comparison you can add array_diff()
if (count(array_unique(array_diff($array,array("")))) < count(array_diff($array,array(""))))
Reference taken from #AndreKR answer from here
Two ways to do it efficiently that I can think of:
inserting all the values into some sort of hashtable and checking whether the value you're inserting is already in it(expected O(n) time and O(n) space)
sorting the array and then checking whether adjacent cells are equal( O(nlogn) time and O(1) or O(n) space depending on the sorting algorithm)
stormdrain's solution would probably be O(n^2), as would any solution which involves scanning the array for each element searching for a duplicate
Find this useful solution
function get_duplicates( $array ) {
return array_unique( array_diff_assoc( $array, array_unique( $array ) ) );
}
After that count result if greater than 0 than duplicates else unique.
I'm using this:
if(count($array)==count(array_count_values($array))){
echo("all values are unique");
}else{
echo("there's dupe values");
}
I don't know if it's the fastest but works pretty good so far
One more solution from me, this is related to performance improvement
$array_count_values = array_count_values($array);
if(is_array($array_count_values) && count($array_count_values)>0)
{
foreach ($array_count_values as $key => $value)
{
if($value>1)
{
// duplicate values found here, write code to handle duplicate values
}
}
}
As you specifically said you didn't want to use array_unique I'm going to ignore the other answers despite the fact they're probably better.
Why don't you use array_count_values() and then check if the resulting array has any value greater than 1?
Php has an function to count the occurrences in the array http://www.php.net/manual/en/function.array-count-values.php
You can do it like that way also:
This will return true if unique else return false.
$nofollow = (count($modelIdArr) !== count(array_unique($modelIdArr))) ? true : false;
The simple solution but quite faster.
$elements = array_merge(range(1,10000000),[1]);
function unique_val_inArray($arr) {
$count = count($arr);
foreach ($arr as $i_1 => $value) {
for($i_2 = $i_1 + 1; $i_2 < $count; $i_2++) {
if($arr[$i_2] === $arr[$i_1]){
return false;
}
}
}
return true;
}
$time = microtime(true);
unique_val_inArray($elements);
echo 'This solution: ', (microtime(true) - $time), 's', PHP_EOL;
Speed - [0.71]!
function hasDuplicate($array){
$d = array();
foreach($array as $elements) {
if(!isset($d[$elements])){
$d[$elements] = 1;
}else{
return true;
}
}
return false;
}
I've been banging my head against this problem for a while. I feel like it should be simple, but I'm having a hard time coming up with a solution.
I'm looking to pre-populate a database, and I need to create SQL statements with some foreign key values. It would be tedious to hand-code them, so naturally I decided to do it in code.
What I want are series of arrays that have values as such:
[1]
[2]
[3]
[1,1]
[1,2]
[1,3]
[2,1]
[2,2]
...
[1,1,1]
[1,1,2]
[1,1,3]
...
[3,1,1]
...
[3,3,3]
I want to specify the number of values in the array, and the numerical value at which it causes the preceeding value to roll over.
In the example I gave above, it would be like generate(3,3), since the maximum number of elements is 3, and the highest value is 3.
How could I write some code that would give me this series of arrays?
This is a recursive function that will generate each of the combinations of the ranges up to the maximum value, with elements in each array from 1 to the number specified:
function generate($elements, $maxvalue) {
if ($elements == 0) return array();
$result = array();
foreach (range(1, $maxvalue) as $el) {
$result[] = array($el);
}
foreach (range(1, $maxvalue) as $el) {
foreach (generate($elements - 1, $maxvalue) as $arr) {
$result[] = array($el, ...$arr);
}
}
return $result;
}
$combs = generate(3, 3);
Output is too long to show here but can be seen in this demo on 3v4l.org
Note for PHP < 7.4, replace
$result[] = array($el, ...$arr);
with
$result[] = array_merge(array($el), $arr);
Here's a version using generators (which may be slightly easier on memory than pure arrays):
function generate(int $elementsCount, int $maxValue, array $current = []): \Generator
{
for ($value = 1; $value <= $maxValue; $value++) {
yield [...$current, $value];
}
if ($elementsCount > 1) {
for ($value = 1; $value <= $maxValue; $value++) {
yield from generate($elementsCount - 1, $maxValue, [...$current, $value]);
}
}
}
Exemple usage + debug/print:
$combinations = generate(3, 3);
print_r(iterator_to_array($combinations, false));
Demo
Is there a function like array_fill, that lets me fill an array with the same string ("field_"), combined with an increasing $i variable like this:
private function fields() {
$result = [];
foreach(range(1,44) as $i) {
$result[] = "field_$i";
}
return $result;
}
I know there is a bunch of functions in php and I would love to have this in one line (if possible).
You could use array_map to remove the need for an explicit loop, but I don't think putting it on one line gains you much in terms of readability:
$results = array_map(function ($i) { return "field_{$i}"; }, range(1, 44));
=
Array
(
[0] => field_1
[1] => field_2
...
I found this, seems to be 3 times faster than foreach and it's the shortest solution I could find so far.
$prefixed_array = preg_filter('/^/', 'prefix_', $array);
Php's foreach loops actually allow you to access the current index
private function fields() {
$result = [];
foreach(range(1,44) as $k => $i) {
$result[] = "field_$k";
}
return $result;
}
I'm sure this is an extremely obvious question, and that there's a function that does exactly this, but I can't seem to find it. In PHP, I'd like to know if my array has duplicates in it, as efficiently as possible. I don't want to remove them like array_unique does, and I don't particularly want to run array_unique and compare it to the original array to see if they're the same, as this seems very inefficient. As far as performance is concerned, the "expected condition" is that the array has no duplicates.
I'd just like to be able to do something like
if (no_dupes($array))
// this deals with arrays without duplicates
else
// this deals with arrays with duplicates
Is there any obvious function I'm not thinking of?
How to detect duplicate values in PHP array?
has the right title, and is a very similar question, however if you actually read the question, he's looking for array_count_values.
I know you are not after array_unique(). However, you will not find a magical obvious function nor will writing one be faster than making use of the native functions.
I propose:
function array_has_dupes($array) {
// streamline per #Felix
return count($array) !== count(array_unique($array));
}
Adjust the second parameter of array_unique() to meet your comparison needs.
Performance-Optimized Solution
If you care about performance and micro-optimizations, check this one-liner:
function no_dupes(array $input_array) {
return count($input_array) === count(array_flip($input_array));
}
Description:
Function compares number of array elements in $input_array with array_flip'ed elements. Values become keys and guess what - keys must be unique in associative arrays so not unique values are lost and final number of elements is lower than original.
Warning:
As noted in the manual, array keys can be only type of int or string so this is what you must have in original array values to compare, otherwise PHP will start casting with unexpected results. See https://3v4l.org/7bRXI for an example of this fringe-case failure mode.
Proof for an array with 10 million records:
The top-voted solution by Jason McCreary: 14.187316179276s ππππππππππππππ
The accepted solution by Mike Sherov: 2.0736091136932s ππ
This answer's solution: 0.14155888557434s π/10
Test case:
<?php
$elements = array_merge(range(1,10000000),[1]);
$time = microtime(true);
accepted_solution($elements);
echo 'Accepted solution: ', (microtime(true) - $time), 's', PHP_EOL;
$time = microtime(true);
most_voted_solution($elements);
echo 'Most voted solution: ', (microtime(true) - $time), 's', PHP_EOL;
$time = microtime(true);
this_answer_solution($elements);
echo 'This answer solution: ', (microtime(true) - $time), 's', PHP_EOL;
function accepted_solution($array){
$dupe_array = array();
foreach($array as $val){
// sorry, but I had to add below line to remove millions of notices
if(!isset($dupe_array[$val])){$dupe_array[$val]=0;}
if(++$dupe_array[$val] > 1){
return true;
}
}
return false;
}
function most_voted_solution($array) {
return count($array) !== count(array_unique($array));
}
function this_answer_solution(array $input_array) {
return count($input_array) === count(array_flip($input_array));
}
Notice that accepted solution might be faster in certain condition when not unique values are near the beginning of huge array.
You can do:
function has_dupes($array) {
$dupe_array = array();
foreach ($array as $val) {
if (++$dupe_array[$val] > 1) {
return true;
}
}
return false;
}
$hasDuplicates = count($array) > count(array_unique($array));
Will be true if duplicates, or false if no duplicates.
Here's my take on this⦠after some benchmarking, I found this to be the fastest method for this.
function has_duplicates( $array ) {
return count( array_keys( array_flip( $array ) ) ) !== count( $array );
}
β¦or depending on circumstances this could be marginally faster.
function has_duplicates( $array ) {
$array = array_count_values( $array );
rsort( $array );
return $array[0] > 1;
}
$duplicate = false;
if(count(array) != count(array_unique(array))){
$duplicate = true;
}
Keep it simple, silly! ;)
Simple OR logic...
function checkDuplicatesInArray($array){
$duplicates=FALSE;
foreach($array as $k=>$i){
if(!isset($value_{$i})){
$value_{$i}=TRUE;
}
else{
$duplicates|=TRUE;
}
}
return ($duplicates);
}
Regards!
To remove all the empty values from the comparison you can add array_diff()
if (count(array_unique(array_diff($array,array("")))) < count(array_diff($array,array(""))))
Reference taken from #AndreKR answer from here
Two ways to do it efficiently that I can think of:
inserting all the values into some sort of hashtable and checking whether the value you're inserting is already in it(expected O(n) time and O(n) space)
sorting the array and then checking whether adjacent cells are equal( O(nlogn) time and O(1) or O(n) space depending on the sorting algorithm)
stormdrain's solution would probably be O(n^2), as would any solution which involves scanning the array for each element searching for a duplicate
Find this useful solution
function get_duplicates( $array ) {
return array_unique( array_diff_assoc( $array, array_unique( $array ) ) );
}
After that count result if greater than 0 than duplicates else unique.
I'm using this:
if(count($array)==count(array_count_values($array))){
echo("all values are unique");
}else{
echo("there's dupe values");
}
I don't know if it's the fastest but works pretty good so far
One more solution from me, this is related to performance improvement
$array_count_values = array_count_values($array);
if(is_array($array_count_values) && count($array_count_values)>0)
{
foreach ($array_count_values as $key => $value)
{
if($value>1)
{
// duplicate values found here, write code to handle duplicate values
}
}
}
As you specifically said you didn't want to use array_unique I'm going to ignore the other answers despite the fact they're probably better.
Why don't you use array_count_values() and then check if the resulting array has any value greater than 1?
Php has an function to count the occurrences in the array http://www.php.net/manual/en/function.array-count-values.php
You can do it like that way also:
This will return true if unique else return false.
$nofollow = (count($modelIdArr) !== count(array_unique($modelIdArr))) ? true : false;
The simple solution but quite faster.
$elements = array_merge(range(1,10000000),[1]);
function unique_val_inArray($arr) {
$count = count($arr);
foreach ($arr as $i_1 => $value) {
for($i_2 = $i_1 + 1; $i_2 < $count; $i_2++) {
if($arr[$i_2] === $arr[$i_1]){
return false;
}
}
}
return true;
}
$time = microtime(true);
unique_val_inArray($elements);
echo 'This solution: ', (microtime(true) - $time), 's', PHP_EOL;
Speed - [0.71]!
function hasDuplicate($array){
$d = array();
foreach($array as $elements) {
if(!isset($d[$elements])){
$d[$elements] = 1;
}else{
return true;
}
}
return false;
}
I need to combine two foreach statement into one for example
foreach ($categories_stack as $category)
foreach ($page_name as $value)
I need to add these into the same foreach statement
Is this possible if so how?
(I am not sure I have understood your question completely. I am assuming that you want to iterate through the two lists in parallel)
You can do it using for loop as follows :
$n = min(count($category), count($value));
for($c = 0; $c < $n; $c = $c + 1){
$categories_stack = $category[$c];
$pagename = $value[$c];
...
}
To achieve the same with foreach you need a function similar to Python's zip() function.
In Python, it would be :
for categories_stack, pagename in zip(categories, values):
print categories_stack, pagename
Since PHP doesn't have a standard zip() function, you'll have to write such a function on your own or go with the for loop solution.
You can do nested foreachs if that's what you want. But without knowing more of your data, it's impossible to say if this helps:
foreach ($categories_stack as $category) {
foreach ($page_name as $value) {
}
}
Probably you want to print out all pages in a category? That probably won't work, so can you give a bit more info on how the arrays look like and relate to each other?
This loop will continue to the length of the longest array and return null for where there are no matching elements in either of the arrays. Try it out!
$a = array(1 => "a",25 => "b", 10 => "c",99=>"d");
$b = array(15=>1,5=>2,6=>3);
$ao = new ArrayObject($a);
$bo = new ArrayObject($b);
$ai = $ao->getIterator();
$bi = $bo->getIterator();
for (
$ai->rewind(),$bi->rewind(),$av = $ai->current(),$bv = $bi->current();
list($av,$bv) =
array(
($ai->valid() ? $ai->current() : null),
($bi->valid() ? $bi->current() : null)
),
($ai->valid() || $bi->valid());
($ai->valid() ? $ai->next() : null),($bi->valid() ? $bi->next() : null))
{
echo "\$av = $av\n";
echo "\$bv = $bv\n";
}
I cannot really tell from the question exactly how you want to traverse the two arrays. For a nested foreach you simply write
foreach ($myArray as $k => $v) {
foreach ($mySecondArray as $kb => $vb {
}
}
However you can do all sorts of things with some creative use of callback functions. In this case an anonymous function returning two items from each array on each iteration. It's then easy to use the iteration value as an array or split it into variables using list() as done below.
This also has the added benefit of working regardless of key structure. I's purely based on the ordering of array elements. Just use the appropriate sorting function if the elements are out of order.
It does not worry about the length of the arrays as there is no error reported, so make sure you keep an eye out for empty values.
$a = array("a","b","c");
$b = array(1,2,3);
foreach (
array_map(
create_function(
'$a,$b', 'return array($a,$b);'
)
,$a,$b
)
as $value
)
{
list($a,$b) = $value;
echo "\$a = $a\n";
echo "\$b = $b\n";
}
Output
$a = a
$b = 1
$a = b
$b = 2
$a = c
$b = 3
Here's another one for you that stops on either of the lists ending. Same as using min(count(a),count(b). Useful if you have arrays of same length. If someone can make it continue to the max(count(a),count(b)) let me know.
$ao = new ArrayObject($a);
$bo = new ArrayObject($b);
$ai = $ao->getIterator();
$bi = $bo->getIterator();
for (
$ai->rewind(),$bi->rewind();
$av = $ai->current(),$bv=$bi->current();
$ai->next(),$bi->next())
{
echo "\$av = $av\n";
echo "\$bv = $bv\n";
}
This is where the venerable for loop comes in handy:
for(
$i = 0,
$n = sizeof($categories_stack),
$m = sizeof($page_name);
$i < $n && $i < $m;
$i++
) {
$category = $categories_stack[$i];
$value = $page_name[$i];
// do stuff here ....
}
Surely you can just merge the arrays before looping?
$data = array_merge($categories_stack, $page_name);
foreach($data AS $item){
...
}
Do the array elements have a direct correspondence with one another, i.e. is there an element in $page_name for each element in $categories_stack? If so, just iterate over the keys and values (assuming they have the same keys):
foreach ($categories_stack as $key => $value)
{
$category = $value;
$page = $page_name[$key];
// ...
}
Could you just nest them with variables outside the scope of the foreach, or prehaps store the content as an array similar to a KVP setup? My answer is vague but I'm not really sure why you're trying to accomplish this.