PHP array with url values to new array with combin

2020-04-27 07:47发布

问题:

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.

回答1:

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



回答2:

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



回答3:

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.



标签: php arrays