Reputation: 519
Given two strings, what is the best approach in PHP to retrieve the characters which are in common, and those which are not?
For instance, given the two strings:
postcard
car
I would like to get something like:
letters in common: c - a - r
letters not in common: p - o - s - t - d
I saw there are word similarity functions which return a numeric value.
However, I would like to retrieve each single char.
My approach would be to treat both strings as arrays (using str_split
) and then check if the elements in the shortest string are present in the longer one (using in_array
).
$arr_postcard = str_split($postcard);
$arr_car = str_split($car);
$notcommon = array();
if (in_array($arr_postcard, $arr_car) == false){
array_push($notcommon, $arr_car);
}
foreach($notcommon as $k => $v){
print_r ($v);
}
The code above doesn't seem to work. It returns the values of $arr_car
.
Maybe there are other ways.
Upvotes: 1
Views: 1231
Reputation: 807
I would go for something as the following. Split the string to obtain each individual character, and flip the keys with the values such that all individual characters are keys. (using array_flip)
Now we can use the keys with some basic set operations such as the array_intersect_key to get the intersection to obtain characters in both strings. We can apply array_diff_key to get the difference of sets (thus those characters in the first string but not in the other).
$s1 = array_flip(str_split('postcard'));
$s2 = array_flip(str_split('car'));
$intersection = array_intersect_key($s1, $s2);
$difference = array_diff_key($s1, $s2);
echo 'Letters in common: ' . implode(' - ', array_keys($intersection)) . PHP_EOL;
echo 'Letters NOT in common: ' . implode(' - ', array_keys($difference)) . PHP_EOL;
The above was indeed for spitting out unique characters (note set). Below a piece of code I assume is what you are trying to achieve:
function outputResult(string $s, bool $inCommon = true)
{
$result = 'Letters';
if (!$inCommon) {
$result .= ' NOT';
}
$result .= ' in common: ';
$result .= !empty($s) ? implode(' - ', str_split($s)) : 'NONE';
echo $result . PHP_EOL;
}
// Count for both the occurrences of each char.
$s1 = array_count_values(str_split('paccoi'));
$s2 = array_count_values(str_split('coi'));
$mostUniqueChars = $s1;
$leastUniqueChars = $s2;
// For now I assumed the string with most unique characters
// is the one you want to test. Could ofcourse output them both
// ways if you wrap all logic in a function. (note that intersection
// is the same both ways)
if (count($s2) > count($s1)) {
$mostUniqueChars = $s2;
$leastUniqueChars = $s1;
}
$intersect = '';
$diff = '';
foreach ($mostUniqueChars as $char => $count) {
// Get the number of characters in common (and how frequent)
$common = min($count, ($leastUniqueChars[$char] ?? 0));
// As an alternative you could add common and difference to an array to keep
// the counts, but I chose to repeat it and concat it to a string.
if ($common > 0) {
$intersect .= str_repeat($char, $common);
}
// Calculate the difference between first string and second string
// in case difference has a value <= 0 then string 2 had more occurrences
// of the character.
$difference = $count - ($leastUniqueChars[$char] ?? 0);
if ($difference > 0) {
$diff .= str_repeat($char, $difference);
}
};
// Note that both strings $intersect and $diff contain
// all the characters, you could also output these directly.
outputResult($intersect);
outputResult($diff, $inCommon = false);
Outputs:
Letters in common: c - o - i
Letters NOT in common: p - a - c
Upvotes: 1
Reputation: 72299
A simple way to do so:
<?php
$postcard = 'paccoi';
$car = 'coi';
$arr_postcard = str_split($postcard);
$arr_car = str_split($car);
function array_diff_once($array1, $array2) {
foreach($array2 as $a) {
$pos = array_search($a, $array1);
if($pos !== false) {
unset($array1[$pos]);
}
}
return $array1;
}
$uncommon = count($arr_postcard) >= count($arr_car) ? array_diff_once($arr_postcard,$arr_car) : array_diff_once($arr_car,$arr_postcard);
echo 'Letters not in common: ' . implode(' - ', $uncommon) . PHP_EOL;
function array_intersect_once($array1, $array2) {
$array = [];
foreach($array1 as $a) {
$pos = array_search($a, $array2);
if($pos !== false) {
$array[] = $a;
}
}
return $array;
}
$common = count($arr_postcard) >= count($arr_car) ? array_intersect_once($arr_car,$arr_postcard) : array_intersect_once($arr_postcard,$arr_car);
echo 'Letters in common: ' . implode(' - ', $common) . PHP_EOL;
Output: https://3v4l.org/lY755 and https://3v4l.org/kK9sE
Note:- you can use trim()
inside str_split()
to overcome the problem of string with leading or trailing spaces.
Reference taken: Keep Duplicates while Using array_diff
Upvotes: 1