PHP-массив с значениями url для нового массива с комбинированными значениями

Я давно пробовал, но не смог найти способ объединить массив в новый. В основном я теряюсь в циклах и сопоставлении.; (

Я хотел бы получить метод php 5, который может сделать следующее:

Пример 1

Допустим, есть массив с URL-адресами:

Array( 'a', 'a/b/c', 'a/b/c/d/e', 'a/y', 'b/z', 'b/z/q/', ) 

Каждая последняя папка url – это папка, в которой пользователь имеет право просматривать.

Я хотел бы отправить массив методу, который возвращает новый массив, например:

 Array[]( 'a/c/e' 'a/y' 'z/q' ) 

Метод объединил некоторые элементы массива origninal в один элемент. Это потому, что есть совпадение в разрешенных конечных папках.

Пример 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' ) 

Я хотел бы получить массив вроде:

 Array[]( 'books/book1' 'wall' 'wall' 'projectZ/Bacon' ) 

Тогда было бы здорово (особенно в случае значений «стены») иметь некоторые ссылки на полный путь исходного массива.

Сделайте это, как показано ниже:

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

Выход: – https://eval.in/846738

Ну, для этого нет единой встроенной функции;)

 $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 не $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

Вы можете избежать использования вложенных циклов (и, на самом деле, вам следует избегать):

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

Вот рабочая демонстрация .

Мы используем array_reduce здесь, чтобы получить доступ к ранее обработанному элементу. Кроме того, PHP имеет basename функции, которое извлекает базовое имя. Поэтому вы можете использовать его и не изобретать велосипед.