Consider the following array:
/www/htdocs/1/sites/lib/abcdedd
/www/htdocs/1/sites/conf/xyz
/www/htdocs/1/sites/conf/abc/
Well, there are already some solutions here but, just because it was fun:
$values = array(
'/www/htdocs/1/sites/lib/abcdedd',
'/www/htdocs/1/sites/conf/xyz',
'/www/htdocs/1/sites/conf/abc/def',
'/www/htdocs/1/sites/htdocs/xyz',
'/www/htdocs/1/sites/lib2/abcdedd'
);
function findCommon($values){
$common = false;
foreach($values as &$p){
$p = explode('/', $p);
if(!$common){
$common = $p;
} else {
$common = array_intersect_assoc($common, $p);
}
}
return $common;
}
function removeCommon($values, $common){
foreach($values as &$p){
$p = explode('/', $p);
$p = array_diff_assoc($p, $common);
$p = implode('/', $p);
}
return $values;
}
echo '<pre>';
print_r(removeCommon($values, findCommon($values)));
echo '</pre>';
Output:
Array
(
[0] => lib/abcdedd
[1] => conf/xyz
[2] => conf/abc/def
[3] => htdocs/xyz
[4] => lib2/abcdedd
)
You could remove prefix the fastest way, reading each character only once:
function findLongestWord($lines, $delim = "/")
{
$max = 0;
$len = strlen($lines[0]);
// read first string once
for($i = 0; $i < $len; $i++) {
for($n = 1; $n < count($lines); $n++) {
if($lines[0][$i] != $lines[$n][$i]) {
// we've found a difference between current token
// stop search:
return $max;
}
}
if($lines[0][$i] == $delim) {
// we've found a complete token:
$max = $i + 1;
}
}
return $max;
}
$max = findLongestWord($lines);
// cut prefix of len "max"
for($n = 0; $n < count($lines); $n++) {
$lines[$n] = substr(lines[$n], $max, $len);
}
I'll throw my hat in the ring …
function longestCommonPrefix($a, $b) {
$i = 0;
$end = min(strlen($a), strlen($b));
while ($i < $end && $a[$i] == $b[$i]) $i++;
return substr($a, 0, $i);
}
function longestCommonPrefixFromArray(array $strings) {
$count = count($strings);
if (!$count) return '';
$prefix = reset($strings);
for ($i = 1; $i < $count; $i++)
$prefix = longestCommonPrefix($prefix, $strings[$i]);
return $prefix;
}
function stripPrefix(&$string, $foo, $length) {
$string = substr($string, $length);
}
Usage:
$paths = array(
'/www/htdocs/1/sites/lib/abcdedd',
'/www/htdocs/1/sites/conf/xyz',
'/www/htdocs/1/sites/conf/abc/def',
'/www/htdocs/1/sites/htdocs/xyz',
'/www/htdocs/1/sites/lib2/abcdedd',
);
$longComPref = longestCommonPrefixFromArray($paths);
array_walk($paths, 'stripPrefix', strlen($longComPref));
print_r($paths);
A naive approach would be to explode the paths at the /
and successive compare every element in the arrays. So e.g. the first element would be empty in all arrays, so it will be removed, the next element will be www
, it is the same in all arrays, so it gets removed, etc.
Something like (untested)
$exploded_paths = array();
foreach($paths as $path) {
$exploded_paths[] = explode('/', $path);
}
$equal = true;
$ref = &$exploded_paths[0]; // compare against the first path for simplicity
while($equal) {
foreach($exploded_paths as $path_parts) {
if($path_parts[0] !== $ref[0]) {
$equal = false;
break;
}
}
if($equal) {
foreach($exploded_paths as &$path_parts) {
array_shift($path_parts); // remove the first element
}
}
}
Afterwards you just have to implode the elements in $exploded_paths
again:
function impl($arr) {
return '/' . implode('/', $arr);
}
$paths = array_map('impl', $exploded_paths);
Which gives me:
Array
(
[0] => /lib/abcdedd
[1] => /conf/xyz
[2] => /conf/abc/def
[3] => /htdocs/xyz
[4] => /conf/xyz
)
This might not scale well ;)