Find and replace duplicates in Array - php

I need to make app with will fill array with some random values, but if in array are duplicates my app not working correctly. So I need to write script code which will find duplicates and replace them with some other values.
Okay so for example i have an array:
<?PHP
$charset=array(123,78111,0000,123,900,134,00000,900);
function arrayDupFindAndReplace($array){
// if in array are duplicated values then -> Replace duplicates with some other numbers which ones I'm able to specify.
return $ArrayWithReplacedValues;
}
?>
So result shall be the same array with replaced duplicated values.

You can just keep track of the words that you've seen so far and replace as you go.
// words we've seen so far
$words_so_far = array();
// for each word, check if we've encountered it so far
// - if not, add it to our list
// - if yes, replace it
foreach($charset as $k => $word){
if(in_array($word, $words_so_far)){
$charset[$k] = $your_replacement_here;
}
else {
$words_so_far[] = $word;
}
}
For a somewhat-optimized solution (for cases where there are not that many duplicates), use array_count_values() (reference here) to count the number of times it shows up.
// counts the number of words
$word_count = array_count_values($charset);
// words we've seen so far
$words_so_far = array();
// for each word, check if we've encountered it so far
// - if not, add it to our list
// - if yes, replace it
foreach($charset as $k => $word){
if($word_count[$word] > 1 && in_array($word, $words_so_far)){
$charset[$k] = $your_replacement_here;
}
elseif($word_count[$word] > 1){
$words_so_far[] = $word;
}
}

Here the example how to generate unique values and replace recurring values in array
function get_unique_val($val, $arr) {
if ( in_array($val, $arr) ) {
$d = 2; // initial prefix
preg_match("~_([\d])$~", $val, $matches); // check if value has prefix
$d = $matches ? (int)$matches[1]+1 : $d; // increment prefix if exists
preg_match("~(.*)_[\d]$~", $val, $matches);
$newval = (in_array($val, $arr)) ? get_unique_val($matches ? $matches[1].'_'.$d : $val.'_'.$d, $arr) : $val;
return $newval;
} else {
return $val;
}
}
function unique_arr($arr) {
$_arr = array();
foreach ( $arr as $k => $v ) {
$arr[$k] = get_unique_val($v, $_arr);
$_arr[$k] = $arr[$k];
}
unset($_arr);
return $arr;
}
$ini_arr = array('dd', 'ss', 'ff', 'nn', 'dd', 'ff', 'vv', 'dd');
$res_arr = unique_arr($ini_arr); //array('dd', 'ss', 'ff', 'nn', 'dd_2', 'ff_2', 'vv', 'dd_3');
Full example you can see here webbystep.ru

Use the function
array_unique()
See more info at http://php.net/manual/en/function.array-unique.php

$uniques = array();
foreach ($charset as $value)
$uniques[$value] = true;
$charset = array_flip($uniques);

Related

PHP array with url values to new array with combined values

I have tried for a long time but couldn't find a way to merge an array in to a new one.
Mostly I get lost in looping and matching.;(
I would like to recieve a php 5 method that can do the following:
Example 1
Lets say there is an array with url's like:
Array(
'a',
'a/b/c',
'a/b/c/d/e',
'a/y',
'b/z',
'b/z/q/',
)
Every last folder of the url's is the folder where a user has the right to view.
I would like to send the array to a method that returns a new array like:
Array[](
'a/c/e'
'a/y'
'z/q'
)
The method has combined some elements of the origninal array into one element.
This because there is a match in allowed ending folders.
Example 2
Array(
'projects/projectA/books'
'projects/projectA/books/cooking/book1'
'projects/projectA/walls/wall'
'projects/projectX/walls/wall'
'projects/projectZ/'
'projects/projectZ/Wood/Cheese/Bacon'
)
I would like to get a an array like:
Array[](
'books/book1'
'wall'
'wall'
'projectZ/Bacon'
)
Then it would be great (specialy in case of the 'wall' values) to have some references to the full path's of the original array.
Do it like below:-
<?php
$array = Array(
'projects/projectA/books',
'projects/projectA/books/cooking/book1',
'projects/projectA/walls/wall',
'projects/projectX/walls/wall',
'projects/projectZ/',
'projects/projectZ/Wood/Cheese/Bacon'
);// original array
$final_array =array(); // new array variable
foreach($array as $key=>$arr){ // iterate over original array
$exploded_string = end(array_filter(explode('/',$arr))); // get last-value from the url string
foreach($array as $ar){ // iterate again the original array to compare this string withh each array element
$new_exploded_string = end(array_filter(explode('/',$ar))); // get the new-last-values from url string again
if($arr !== $ar && strpos($ar,$exploded_string) !==false){ // if both old and new url strings are not equal and old-last-value find into url string
if($exploded_string == $new_exploded_string ){ // if both new-last-value and old-last-value are equal
$final_array[] = $exploded_string;
}else{
$final_array[] = $exploded_string.'/'.$new_exploded_string ;
}
}
}
}
print_r($final_array);
Output:-https://eval.in/846738
Well, there isn't a single built-in function for this ;)
$items = array(
'projects/projectA/books',
'projects/projectA/books/cooking/book1',
'projects/projectA/walls/wall',
'projects/projectX/walls/wall',
'projects/projectZ/',
'projects/projectZ/Wood/Cheese/Bacon',
'hold/mold/gold/sold/fold',
'hold/mold/gold',
'raja/maza/saza',
'raja/maza',
'mohit/yenky/client/project',
);
echo '$items = ' . nl2br(htmlspecialchars(print_r($items, true))); //Debug
// Sort, so the shorter basePath comes before the longer subPath
usort($items, function($a, $b) {
if (strlen($a) == strlen($b)) {
return 0;
} else {
return strlen($a) > strlen($b) ? 1 : -1;
}
});
$result = array();
while($basePath = array_shift($items)) { // As long as there is a next item
$basePath = rtrim($basePath, '/'); // Right trim extra /
foreach($items as $idx => $subPath) {
if (strpos($subPath, $basePath . '/') === 0) {
// $subPath begins with $basePath
$result[] = preg_replace('#.*/#', '', $basePath) . '/' . preg_replace('#.*/#', '', rtrim($subPath, '/'));
unset($items[$idx]); // Remove item from array, so it won't be matched again
continue 2; // Continue with next while($basePath = array_shift($items))
}
}
// No subPath found, otherwise continue would have called (skipping below code)
$result[] = preg_replace('#.*/#', '', $basePath);
}
echo '$result = ' . nl2br(htmlspecialchars(print_r($result, true))); //Debug
PHPFiddle: http://phpfiddle.org/main/code/ugq9-hy0i
You can avoid using nested loops (and, actually, you should avoid):
sort($array);
$carry = array_shift($array);
$result = [];
$i = 0;
$lastItem = array_reduce($array, function ($carry, $item) use (&$result, &$i) {
$result[$i] = isset($result[$i])
? array_merge($result[$i], [basename($carry)])
: [basename($carry)];
if (strpos($item, $carry) !== 0) {
$i += 1;
}
return $item;
}, $carry);
if (!empty($lastItem)) {
$result[$i] = isset($result[$i])
? array_merge($result[$i], [basename($lastItem)])
: [basename($lastItem)];
}
$result = array_map(function ($item) {
return implode('/', $item);
}, $result);
Here is working demo.
We use array_reduce here to get access to the previously processed item. Also, PHP has function basename, that retrieves the basename. So you can use it and do not reinvent the wheel.

Which member of array does the string contain in PHP?

How can I check if a string contains a member of an array, and return the index (integer) of the relevant member?
Let's say my string is this :
$string1 = "stackoverflow.com";
$string2 = "superuser.com";
$r = array("queue" , "stack" , "heap");
get_index($string1 , $r); // returns 1
get_index($string2 , $r); // returns -1 since string2 does not contain any element of array
How can I write this function in an elegant (short) and efficient way ?
I found a function (expression ? ) that checks if the string contains a member of an array :
(0 < count(array_intersect(array_map('strtolower', explode(' ', $string)), $array)))
but this is a boolean. does the count() function return what I want in this statement ?
Thanks for any help !
function get_index($str, $arr){
foreach($arr as $key => $val){
if(strpos($str, $val) !== false)
return $key;
}
return -1;
}
Demo: https://eval.in/95398
This will find the number of matching elements in your array, if you want all matching keys, use the commented lines instead:
function findMatchingItems($needle, $haystack){
$foundItems = 0; // start counter
// $foundItems = array(); // start array to save ALL keys
foreach($haystack as $key=>$value){ // start to loop through all items
if( strpos($value, $needle)!==false){
++$foundItems; // if found, increase counter
// $foundItems[] = $key; // Add the key to the array
}
}
return $foundItems; // return found items
}
findMatchingItems($string1 , $r);
findMatchingItems($string2 , $r);
If you want to return all matching keys, just change $foundItems to an array and add the keys in the if-statement (switch to the commented lines).
If you only want to know if something matches or not
function findMatchingItems($needle, $haystack){
if( strpos($value, $needle)!==false){
return true;
break; // <- This is important. This stops the loop, saving time ;)
}
return false;// failsave, if no true is returned, this will return
}
I would do a function like this:
function getIndex($string, $array) {
$index = -1;
$i = 0;
foreach($array as $array_elem) {
if(str_pos($array_elem, $string) !== false) {
$index = $i;
}
$i++;
}
return $index;
}

Form a new string with data from an array PHP

I would need to reduce the quantity of these numbers and present them in a more concise way, instead of presenting several lines of numbers with the same "prefix" or "root". For example:
If I have an array like this, with several strings of numbers (obs: only numbers and the array is already sorted):
$array = array(
"12345647",
"12345648",
"12345649",
"12345657",
"12345658",
"12345659",
);
The string: 123456 is the same in all elements of the array, so it would be the root or the prefix of the number. According to the above array I would get a result like this:
//The numbers in brackets represent the sequence of the following numbers,
//instead of showing the rows, I present all the above numbers in just one row:
$stringFormed = "123456[4-5][7-9]";
Another example:
$array2 = array(
"1234",
"1235",
"1236",
"1247",
"2310",
"2311",
);
From the second array, I should get a result like this:
$stringFormed1 = "123[4-7]";
$stringFormed2 = "1247";
$stringFormed3 = "231[0-1]";
Any idea?
$array = array(
"12345647",
"12345648",
"12345649",
"12345657",
"12345658",
"12345659",
);
//find common string positions for all elements
$res = array();
foreach($array as $arr){
for($i=0;$i<strlen($arr);$i++){
$res[$i][$arr[$i]] = $arr[$i];
}
}
//make final string
foreach($res as $pos){
if(count($pos)==1)
$str .= implode('',$pos);
else{
//u may need to sort these values if you want them in order
$end = end($pos);
$first = reset($pos);
$str .="[$first-$end]";
}
}
echo $str; // "123456[4-5][7-9]";
Well, as I understand you want the final string with unique characters. (i'm not sure if you want it ordered)
So, first implode to create the string
$stringFormed = implode("", $array);
Then we get the unique chars :
$stringFormed=implode("",array_unique(str_split($stringFormed)));
OUTPUT: 123456789
That as a solution for first example but i didn't thought there could be several roots.
By the way i'm not sure it's well coded...
<?php
function longest_common_substring($words)
{
$words = array_map('strtolower', array_map('trim', $words));
$sort_by_strlen = create_function('$a, $b', 'if (strlen($a) == strlen($b)) { return strcmp($a, $b); } return (strlen($a) < strlen($b)) ? -1 : 1;');
usort($words, $sort_by_strlen);
// We have to assume that each string has something in common with the first
// string (post sort), we just need to figure out what the longest common
// string is. If any string DOES NOT have something in common with the first
// string, return false.
$longest_common_substring = array();
$shortest_string = str_split(array_shift($words));
while (sizeof($shortest_string)) {
array_unshift($longest_common_substring, '');
foreach ($shortest_string as $ci => $char) {
foreach ($words as $wi => $word) {
if (!strstr($word, $longest_common_substring[0] . $char)) {
// No match
break 2;
} // if
} // foreach
// we found the current char in each word, so add it to the first longest_common_substring element,
// then start checking again using the next char as well
$longest_common_substring[0].= $char;
} // foreach
// We've finished looping through the entire shortest_string.
// Remove the first char and start all over. Do this until there are no more
// chars to search on.
array_shift($shortest_string);
}
// If we made it here then we've run through everything
usort($longest_common_substring, $sort_by_strlen);
return array_pop($longest_common_substring);
}
$array = array(
"12345647",
"12345648",
"12345649",
"12345657",
"12345658",
"12345659",
);
$result= longest_common_substring($array);
for ($i = strlen($result); $i < strlen($array[0]); $i++) {
$min=intval($array[0][$i]);
$max=$min;
foreach ($array as $string) {
$val = intval($string[$i]);
if($val<$min)
$min=$val;
elseif($val>$max)
$max=$val;
}
$result.='['.$min.'-'.$max.']';
}
echo $result;
?>

PHP Array values in string?

I have been looking around for a while in the PHP manual and can't find any command that does what I want.
I have an array with Keys and Values, example:
$Fields = array("Color"=>"Bl","Taste"=>"Good","Height"=>"Tall");
Then I have a string, for example:
$Headline = "My black coffee is cold";
Now I want to find out if any of the array ($Fields) values match somewhere in the string ($Headline).
Example:
Array_function_xxx($Headline,$Fields);
Would give the result true because "bl" is in the string $Headline (as a part of "Black").
I'm asking because I need performance... If this isn't possible, I will just make my own function instead...
EDIT - I'm looking for something like stristr(string $haystack , array $needle);
Thanks
SOLUTION - I came up with his function.
function array_in_str($fString, $fArray) {
$rMatch = array();
foreach($fArray as $Value) {
$Pos = stripos($fString,$Value);
if($Pos !== false)
// Add whatever information you need
$rMatch[] = array( "Start"=>$Pos,
"End"=>$Pos+strlen($Value)-1,
"Value"=>$Value
);
}
return $rMatch;
}
The returning array now have information on where each matched word begins and ends.
This should help:
function Array_function_xxx($headline, $fields) {
$field_values = array_values($fields);
foreach ($field_values as $field_value) {
if (strpos($headline, $field_value) !== false) {
return true; // field value found in a string
}
}
return false; // nothing found during the loop
}
Replace name of the function with what you need.
EDIT:
Ok, alternative solution (probably giving better performance, allowing for case-insensitive search, but requiring proper values within $fields parameter) is:
function Array_function_xxx($headline, $fields) {
$regexp = '/(' . implode('|',array_values($fields)) . ')/i';
return (bool) preg_match($regexp, $headline);
}
http://www.php.net/manual/en/function.array-search.php
that's what you looking for
example from php.net
<?php
$array = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red');
$key = array_search('green', $array); // $key = 2;
$key = array_search('red', $array); // $key = 1;
?>

Return only duplicated entries from an array (case-insensitive)

I want to retrieve all case-insensitive duplicate entries from an array. Is this possible in PHP?
array(
1 => '1233',
2 => '12334',
3 => 'Hello',
4 => 'hello',
5 => 'U'
);
Desired output array:
array(
1 => 'Hello',
2 => 'hello'
);
function get_duplicates ($array) {
return array_unique( array_diff_assoc( $array, array_unique( $array ) ) );
}
<?php
function array_not_unique($raw_array) {
$dupes = array();
natcasesort($raw_array);
reset($raw_array);
$old_key = NULL;
$old_value = NULL;
foreach ($raw_array as $key => $value) {
if ($value === NULL) { continue; }
if (strcasecmp($old_value, $value) === 0) {
$dupes[$old_key] = $old_value;
$dupes[$key] = $value;
}
$old_value = $value;
$old_key = $key;
}
return $dupes;
}
$raw_array = array();
$raw_array[1] = 'abc#xyz.com';
$raw_array[2] = 'def#xyz.com';
$raw_array[3] = 'ghi#xyz.com';
$raw_array[4] = 'abc#xyz.com'; // Duplicate
$common_stuff = array_not_unique($raw_array);
var_dump($common_stuff);
You will need to make your function case insensitive to get the "Hello" => "hello" result you are looking for, try this method:
$arr = array(1=>'1233',2=>'12334',3 =>'Hello' ,4=>'hello', 5=>'U');
// Convert every value to uppercase, and remove duplicate values
$withoutDuplicates = array_unique(array_map("strtoupper", $arr));
// The difference in the original array, and the $withoutDuplicates array
// will be the duplicate values
$duplicates = array_diff($arr, $withoutDuplicates);
print_r($duplicates);
Output is:
Array
(
[3] => Hello
[4] => hello
)
Edit by #AlixAxel:
This answer is very misleading. It only works in this specific condition. This counter-example:
$arr = array(1=>'1233',2=>'12334',3 =>'Hello' ,4=>'HELLO', 5=>'U');
Fails miserably. Also, this is not the way to keep duplicates:
array_diff($arr, array_unique($arr));
Since one of the duplicated values will be in array_unique, and then chopped off by array_diff.
Edit by #RyanDay:
So look at #Srikanth's or #Bucabay's answer, which work for all cases (look for case insensitive in Bucabay's), not just the test data specified in the question.
This is the correct way to do it (case-sensitive):
array_intersect($arr, array_unique(array_diff_key($arr, array_unique($arr))));
And a case-insensitive solution:
$iArr = array_map('strtolower', $arr);
$iArr = array_intersect($iArr, array_unique(array_diff_key($iArr, array_unique($iArr))));
array_intersect_key($arr, $iArr);
But #Srikanth answer is more efficient (actually, it's the only one that works correctly besides this one).
function array_not_unique($raw_array) {
$dupes = array();
natcasesort($raw_array);
reset($raw_array);
$old_key = NULL;
$old_value = NULL;
foreach ($raw_array as $key => $value) {
if ($value === NULL) { continue; }
if (strcasecmp($old_value, $value) === 0) {
$dupes[$old_key] = $old_value;
$dupes[$key] = $value;
}
$old_value = $value;
$old_key = $key;
} return $dupes;
}
What Srikanth (john) added but with the case insensitive comparison.
Try:
$arr2 = array_diff_key($arr, array_unique($arr));
case insensitive:
array_diff_key($arr, array_unique(array_map('strtolower', $arr)));
12 year old post and the accepted answer returns a blank array and others are long.
Here is my take for future Googlers that is short and returns ALL duplicate indexes (Indices?).
$myArray = array('fantastic', 'brilliant', 'happy', 'fantastic', 'Happy', 'wow', 'battlefield2042 :(');
function findAllDuplicates(array $array)
{
// Remove this line if you do not need case sensitive.
$array = array_map('strtolower', $array);
// Remove ALL duplicates
$removedDuplicates = array_diff($array, array_diff_assoc($array, array_unique($array)));
return array_keys(array_diff($array, $removedDuplicates));
// Output all keys with duplicates
// array(4) {
// [0]=>int(0)
// [1]=>int(2)
// [2]=>int(3)
// [3]=>int(4)
// }
return array_diff($array, $removedDuplicates);
// Output all duplicates
// array(4) {
// [0]=>string(9) "fantastic"
// [2]=>string(5) "happy"
// [3]=>string(9) "fantastic"
// [4]=>string(5) "happy"
// }
}

Categories