medicalbird
medicalbird

Reputation: 97

PHP array with url values to new array with combined values

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.

Upvotes: 1

Views: 124

Answers (3)

Death-is-the-real-truth
Death-is-the-real-truth

Reputation: 72289

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

Upvotes: 3

sevavietl
sevavietl

Reputation: 3802

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.

Upvotes: 2

Peter van der Wal
Peter van der Wal

Reputation: 11786

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

Upvotes: 2

Related Questions