Tetris-ing an array

后端 未结 16 1392
时光取名叫无心
时光取名叫无心 2021-01-30 15:38

Consider the following array:

/www/htdocs/1/sites/lib/abcdedd
/www/htdocs/1/sites/conf/xyz
/www/htdocs/1/sites/conf/abc/         


        
相关标签:
16条回答
  • 2021-01-30 16:20

    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
    )
    
    0 讨论(0)
  • 2021-01-30 16:21

    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);
    }
    
    0 讨论(0)
  • 2021-01-30 16:21

    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);
    
    0 讨论(0)
  • 2021-01-30 16:22

    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 ;)

    0 讨论(0)
提交回复
热议问题