Verification: a143cc29221c9be0

Php array from to range

Php array from to range

C++

#include

using namespace std;

void partSort(int arr[], int N, int a, int b)

{

    int l = min(a, b);

    int r = max(a, b);

    int temp[r - l + 1];

    int j = 0;

    for (int i = l; i

        temp[j] = arr[i];

        j++;

    }

    sort(temp, temp + r - l + 1);

    j = 0;

    for (int i = l; i

            arr[i] = temp[j];

            j++;

    }

    for (int i = 0; i

            cout " " ;

        }

}

int main()

{

    int arr[] = { 7, 8, 4, 5, 2 } ;

    int a = 1 ;

    int b = 4;

    int N = sizeof(arr) / sizeof(arr[0]);

    partSort(arr, N, a, b);

    return 0;

}

Java

import java.io.*;

import java.util.*;

import java.lang.*;

class GFG {

    static void partSort(int[] arr, int N, int a, int b)

    {

        int l = Math.min(a, b);

        int r = Math.max(a, b);

        int[] temp = new int[r - l + 1];

        int j = 0;

        for (int i = l; i

            temp[j] = arr[i];

            j++;

        }

        Arrays.sort(temp);

        j = 0;

        for (int i = l; i

            arr[i] = temp[j];

            j++;

        }

        for (int i = 0; i

            System.out.print(arr[i] + " ");

        }

    }

    public static void main(String args[])

    {

        int[] arr = { 7, 8, 4, 5, 2 };

        int a = 1, b = 4;

        int N = arr.length;

        partSort(arr, N, a, b);

    }

}

Python3

def partSort(arr, N, a, b):

    l = min(a, b)

    r = max(a, b)

    temp = [0 for i in range(r - l + 1)]

    j = 0

    for i in range(l, r + 1, 1):

        temp[j] = arr[i]

        j += 1

    temp.sort(reverse = False)

    j = 0

    for i in range(l, r + 1, 1):

            arr[i] = temp[j]

            j += 1

    for i in range(0, N, 1):

            print(arr[i], end = " ")

if __name__ == '__main__':

    arr = [7, 8, 4, 5, 2]

    a = 1

    b = 4

    N = len(arr)

    partSort(arr, N, a, b)

C#

using System;

class GFG {

    static void partSort(int[] arr, int N, int a, int b)

    {

        int l = Math.Min(a, b);

        int r = Math.Max(a, b);

        int[] temp = new int[r - l + 1];

        int j = 0;

        for (int i = l; i

            temp[j] = arr[i];

            j++;

        }

        Array.Sort(temp);

        j = 0;

        for (int i = l; i

            arr[i] = temp[j];

            j++;

        }

        for (int i = 0; i

            Console.Write(arr[i] + " ");

        }

    }

    public static void Main()

    {

        int[] arr = { 7, 8, 4, 5, 2 };

        int a = 1, b = 4;

        int N = arr.Length;

        partSort(arr, N, a, b);

    }

}

PHP

# PHP program to sort the

# array in a given index range

function partSort( $arr, $N, $a, $b)

{

    $l = min($a, $b);

     $r = max($a, $b);

    $temp = array();

    $j = 0;

    for ($i = $l; $i $r; $i++) {

        $temp[$j] = $arr[$i];

        $j++;

    }

    sort($temp);

    $j = 0;

    for ($i = $l; $i $r; $i++) {

            $arr[$i] = $temp[$j];

            $j++;

    }

    for ($i = 0; $i $N; $i++) {

            echo $arr[$i]." " ;

        }

}

    $arr = array( 7, 8, 4, 5, 2 ) ;

    $a = 1 ;

    $b = 4;

    $N = count($arr);

    partSort($arr, $N, $a, $b);

    ?>

Javascript

    function partSort(arr, N, a, b)

    {

        let l = Math.min(a, b);

        let r = Math.max(a, b);

        let temp = new Array(r - l + 1);

        temp.fill(0);

        let j = 0;

        for (let i = l; i

            temp[j] = arr[i];

            j++;

        }

        temp.sort(function(a, b){return a - b});

        j = 0;

        for (let i = l; i

            arr[i] = temp[j];

            j++;

        }

        for (let i = 0; i

            document.write(arr[i] + " ");

        }

    }

    let arr = [ 7, 8, 4, 5, 2 ];

    let a = 1, b = 4;

    let N = arr.length;

    partSort(arr, N, a, b);

C++

#include

using namespace std;

    void partSort(int arr[], int N, int a, int b)

    {

        int l = min(a, b);

        int r = max(a, b);

        vectorint> v(arr, arr + N);

        sort(v.begin() + l, v.begin() + r + 1);

        for (int i = 0; i

            cout " ";

    }

    int main()

    {

        int arr[] = { 7, 8, 4, 5, 2 };

        int a = 1, b = 4;

        int N = sizeof(arr)/sizeof(arr[0]);

        partSort(arr, N, a, b);

    }

Java

import java.io.*;

import java.util.*;

import java.lang.*;

class GFG {

    static void partSort(int[] arr, int N, int a, int b)

    {

        int l = Math.min(a, b);

        int r = Math.max(a, b);

        Arrays.sort(arr, l, r + 1);

        for (int i = 0; i

            System.out.print(arr[i] + " ");

    }

    public static void main(String args[])

    {

        int[] arr = { 7, 8, 4, 5, 2 };

        int a = 1, b = 4;

        int N = arr.length;

        partSort(arr, N, a, b);

    }

}

Python3

def partSort(arr, N, a, b):

    l = min(a, b)

    r = max(a, b)

    arr = (arr[0 : l] +

    sorted(arr[l : r + 1]) +

           arr[r : N])

    for i in range(0, N, 1):

            print(arr[i], end = " ")

if __name__ == '__main__':

    arr = [ 7, 8, 4, 5, 2 ]

    a = 1

    b = 4

    N = len(arr)

    partSort(arr, N, a, b)

C++

#include

#include

using namespace std;

float getMin(float arr[], int n)

{

    float res = arr[0];

    for (int i = 1; i

        res = min(res, arr[i]);

    return res;

}

float getMax(float arr[], int n)

{

    float res = arr[0];

    for (int i = 1; i

        res = max(res, arr[i]);

    return res;

}

void findRangeAndCoefficient(float arr[], int n)

{

    float max = getMax(arr, n);

    float min = getMin(arr, n);

    float range = max - min;

    float coeffOfRange = range / (max + min);

    cout "Range : "

    cout "Coefficient of Range : "

}

int main()

{

    float arr[] = { 5, 10, 15 };

    int n = sizeof(arr) / sizeof(arr[0]);

    findRangeAndCoefficient(arr, n);

    return 0;

}

Java

import java.io.*;

class GFG {

static float getMin(float arr[], int n)

{

    float res = arr[0];

    for (int i = 1; i

        res = Math.min(res, arr[i]);

    return res;

}

static float getMax(float arr[], int n)

{

    float res = arr[0];

    for (int i = 1; i

        res = Math.max(res, arr[i]);

    return res;

}

static void findRangeAndCoefficient(float arr[], int n)

{

    float max = getMax(arr, n);

    float min = getMin(arr, n);

    float range = max - min;

    float coeffOfRange = range / (max + min);

    System.out.println("Range : " + range );

    System.out.println("Coefficient of Range : " + coeffOfRange);

}

    public static void main (String[] args) {

    float arr[] = { 5, 10, 15 };

    int n = arr.length;

    findRangeAndCoefficient(arr, n);

    }

}

Python3

def getMin(arr, n):

    res = arr[0]

    for i in range(1, n, 1):

        res = min(res, arr[i])

    return res

def getMax(arr, n):

    res = arr[0]

    for i in range(1, n, 1):

        res = max(res, arr[i])

    return res

def findRangeAndCoefficient(arr, n):

    max = getMax(arr, n)

    min = getMin(arr, n)

    range = max - min

    coeffOfRange = range / (max + min)

    print("Range :", range)

    print("Coefficient of Range :", coeffOfRange)

if __name__ == '__main__':

    arr = [5, 10, 15]

    n = len(arr)

    findRangeAndCoefficient(arr, n)

C#

using System;

public class GFG{

static float getMin(float []arr, int n)

{

    float res = arr[0];

    for (int i = 1; i

        res = Math.Min(res, arr[i]);

    return res;

}

static float getMax(float []arr, int n)

{

    float res = arr[0];

    for (int i = 1; i

        res = Math.Max(res, arr[i]);

    return res;

}

static void findRangeAndCoefficient(float []arr, int n)

{

    float max = getMax(arr, n);

    float min = getMin(arr, n);

    float range = max - min;

    float coeffOfRange = range / (max + min);

    Console.WriteLine ("Range : " + range );

    Console.WriteLine ("Coefficient of Range : " + coeffOfRange);

}

    static public void Main (){

    float []arr = { 5, 10, 15 };

    int n = arr.Length;

    findRangeAndCoefficient(arr, n);

    }

}

PHP

function getMin($arr, $n)

{

    $res = $arr[0];

    for ($i = 1; $i $n; $i++)

        $res = min($res, $arr[$i]);

    return $res;

}

function getMax($arr, $n)

{

    $res = $arr[0];

    for ($i = 1; $i $n; $i++)

        $res = max($res, $arr[$i]);

    return $res;

}

function findRangeAndCoefficient($arr, $n)

{

    $max = getMax($arr, $n);

    $min = getMin($arr, $n);

    $range = $max - $min;

    $coeffOfRange = $range / ($max + $min);

    echo "Range : ", $range, "\n";

    echo "Coefficient of Range : ",

                     $coeffOfRange;

}

$arr = array( 5, 10, 15 );

$n = sizeof($arr);

findRangeAndCoefficient($arr, $n);

?>

Определение и использование

Функция array_column() возвращает значения из одного столбца входного массива.


Синтаксис

array_column(array, column_key, index_key)

Параметр значений

Параметр Описание
array Требуемый. Задает используемый многомерный массив (набор записей). Начиная с PHP 7.0, это также может быть массив объектов.
column_key Требуемый. Целочисленный ключ или строковое имя возвращаемого столбца значений. Этот параметр также может быть NULL для возврата полных массивов (полезно вместе с index_key переиндексировать массив)
index_key Необязательный. Столбец, используемый в качестве индекса/ключей для возвращаемого массива


Технические подробности

Возврат значения: Возвращает массив значений, представляющий один столбец из входного массива
PHP Версия: 5.5+

Объединение массивов

Выделил следующие способы объединить два и более массивов в один:

  • array_merge
  • Оператор …
  • Оператор +
  • array_replace
  • foreach

Рассмотрим каждый из способов с примерами кода.

Использование array_merge

$tags = [];
foreach($products as $product) {
    $tags = array_merge($tags, $product['tags']);
}

https://gist.github.com/zualex/39b3dfe9503b69468307f420089d0c42

Распаковка массива через оператор …

$tags = [];
foreach($products as $product) {
    $tags[] = $product['tags'];
}
$tags = array_merge(...$tags);

https://gist.github.com/zualex/c1bd85a1c2fee5b4c362953211d976ea

Использование оператора +

При использовании оператора + если совпадают индексы, то в результирующем массиве будут только элементы из массива слева от оператора. Чтобы этого избежать потребуется переделать структуру хранения товаров, чтобы в списке тегов был уникальный индекс.

$products = [
    ['id' => 1, 'tags' => ['tag_1' => tag_1', 'tag_2' => 'tag_2', 'tag_3' => 'tag_3']],
    ['id' => 2, 'tags' => ['tag_1' => tag_1', 'tag_2' => 'tag_2', 'tag_3' => 'tag_3']],
];

$tags = [];
foreach($products as $product) {
    $tags += $product['tags'];
}

https://gist.github.com/zualex/902a6f9e9d1f23a7da7cdd3bebf72812

Использование array_replace

В данном примере тоже потребуется использовать структуру хранения товаров, как при использовании оператора +.

$tags = [];
foreach($products as $product) {
    $tags = array_replace($tags, $product['tags']);
}

https://gist.github.com/zualex/b656a5f12992f96edaacf1bb22cd0bf7

Использование foreach

$tags = [];
foreach($products as $product) {
    foreach($product['tags'] as $tag) {
        $tags[] = $tag;
    }
}

https://gist.github.com/zualex/0d101fb8b48701352117cfc253be2762

Анализ производительности

Результаты прогонов в этой таблице: https://docs.google.com/spreadsheets/d/1Va7pg5iaPbXMxkbcQ5sOTco0d316IUzLCOcHq1CrzRo/edit?usp=sharing

Эксперименты показали, что для PHP 8.0 и 7.4 нет существенной разницы между версиями. Из-за этого примеры будут для PHP 7.4.

График PHP 7.4 … Для 6 тегов нет расчетов из-за memory limit во время прогона тестов.

График PHP 7.4 +График PHP 7.4 foreach

Как видно из графиков, ..., +, foreach имеют линейную зависимость.

График PHP 7.4 array_mergeГрафик PHP 7.4 array_replace

array_merge, array_replace - видна квадратичная зависимость. В экспериментах данные имеются только для 10000.

Сравним вместе

Теперь попробуем совместить графики, чтобы увидеть общую картину. Сравнивать буду для PHP 7.4 с использованием 3-х тегов.

График для кол-во от 10 до 500 товаров. График PHP 7.4 всё вместе от 10 до 500 товаров

Как видно, уже начиная от 250, а может и ранее (в экспериментах делал тесты для 10, 250, 500 товаров), array_merge и array_replace отрабатывают дольше всех.

Теперь если посмотреть для 50000 товаров. График PHP 7.4 всё вместе от 10 до 50000 товаровarray_merge и array_replace в рамках теста, смог замерить только для 10000 товаров. Разница существенна, на порядки.

А что с памятью?

График PHP 7.4 память С памятью всё ок, все подходы практически одинаково используют память.

Самый быстрый подход

PHP 7.4 сравнение быстрых подходов В данных эксперимента самым быстрым оказался обычный foreach, но разница столь не существенна, что думаю не стоит делать разницы между ..., + и foreach .

А если без цикла?

Для полноты картины посмотрим на те же подходы, если без цикла объединить 3 больших массива по 100000 элементов в каждом.

$big1 = range(0, 100000);
$big2 = array_fill(100000, 100000, 'string');
$big3 = range(200000, 100000);

Без цикла array_merge

$result = array_merge($big1, $big2, $big3);

https://gist.github.com/zualex/a9f536006c1a24f7c4d4888077dc9fc0

Без цикла Распаковка массива через оператор …

$result = [...$big1, ...$big2, ...$big3];

https://gist.github.com/zualex/a35cd71c38480658a1bbaf6559c4c2fe

Без цикла оператор +

$result = $big1 + $big2 + $big3;

https://gist.github.com/zualex/6e6d9d584cc979b59da517f9078e6559

Без цикла array_replace

$result = array_replace($big1, $big2, $big3);

https://gist.github.com/zualex/4e770098f40f2f101138dda7251f32ca

Без цикла foreach

$result = [];
foreach ($big1 as $key => $value) {
    $result[$key] = $value;
}
foreach ($big2 as $key => $value) {
    $result[$key] = $value;
}
foreach ($big3 as $key => $value) {
    $result[$key] = $value;
}

https://gist.github.com/zualex/4e770098f40f2f101138dda7251f32ca

Анализ данных без циклов

Скрость

График PHP 7.4 без цикла скорость

Память

График PHP 7.4 без цикла память (KB)

Как видно, array_merge быстрее всех справляется с объединением больших массивов без использования циклов. По памяти Чуть экномичнее foreach и array_replace

Вывод

array_merge и array_replace не нужно использовать в циклах, даже в маленьких. При решении данной задачи лучше выбрать ..., + или foreach. Но стоит не забывать отличия в работе array_merge и +.

Но если стоит задача объединить массивы, когда нет циклов, то лучше использовать array_merge.

What Is Recursion?

Before diving into examples, we shall first understand what recursion is. Let’s define it, thanks to the computer science wiki:

A method where the solution to a problem depends on solutions to smaller instances of the same problem.

Decomposing a problem is a very useful technique to solve it without headaches. I spoke already about it when I revisited the Single Responsibility Principle.

In short, decomposing a problem means:

  • Breaking down a problem into smaller and simpler sub-problems.
  • Solving the sub-problems to solve the main problem.

If these sub-problems are all smaller instances of the same problem, it means that a solution to a sub-problem is valid for every other sub-problems. You need to apply the same solution to every single one of them, again and again.

When you reach the smallest sub-problem, your main problem will be solved, too. How cool is that?

How To Do Recursion?

To apply a recursive solution to a problem, you need to go through two steps:

  1. Finding the base case.
  2. Finding the recursive steps.

The Base Case

Recursion can be seen as a reduction from the bigger problem to the simplest, smallest instance of the same problem. The smallest of all sub-problems is called the base case. This is what we should find first.

In the real world, your recursive process will often take the shape of a function. Like any function, it will take some input and give back some output. The base case is the simplest process the function can do for a given input.

The Recursive Step

When you’ve found the base case, you’ve solved the smallest sub-problem, but you still have to solve every other ones.

The recursive step is the reduction we spoke about earlier: applying the same solution to every sub-problem. You’ll reduce the main problem into a chain of smaller sub-problems, until your problem is so simple it reaches the base case.

Recursion In Action

Davina looks at you, desperate, horribly lost. You begin to question as well where I’m heading to while reading this article. You’re right: enough theory! It’s now practice time.

I would advise you to try to solve the problems given in this article. You’ll then understand my theoretical babbling from earlier, and, as a result, how to apply recursion to a wide range of problems. You’ll remember it, too, because you tried it with your own brain.

These exercises are in PHP. If you know already a language with a C-like syntax, it shouldn’t be too hard for you to follow. You can as well try to translate the examples in another language. They are pretty short, so it shouldn’t take weeks.

If you don’t understand anything about PHP, you can still ask me to do them in another language. I would be happy to do it depending on the need.

Sum of Range

Let’s take a simple problem for the beginning: calculating the sum for a range of positive integers, starting from 0.

For example:

  • Sum range to 5: 0 + 1 + 2 + 3 + 4 + 5 = 15
  • Sum range to 10: 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = 55
  • Sum range to n: 0 + 1 + 2 + 3 + 4 + 5 + ... + n = ???

Your challenge, if you accept it, is to write a function which has these properties:

  • The variable n is the argument of the function.
  • The output have to be the sum of the range from 0 to n.

Iterative Solution

Before using recursion, let’s try to solve the problem using the good old iterative for loop. Davina is already solving it, so fire your best editor and let’s go!

function iterativeSumRange($n)
{
    $total = 0;
    for ($i = 0; $i  $n; $i++) {
        $total += $i;
    }

    return $total;
}

echo iterativeSumRange(5);
// => 15

This can be seen as an accumulation: we first go from 0, and then we add the good numbers to $total.

Recursive Solution

To solve the problem using recursion, we need to go through the two steps we saw above.

The Base Case

The base case is the smallest possible sub-problem. What would be the value of n which would make our function trivial?

If n is 0, the sum of range from 0 to 0 is… 0. This is the smallest sub-problem of our main problem. With this base case, we can already write the beginning of our function:

function recursiveSumRange($n) {
    if ($n == 0) {
        return $n;
    }
}
The Recursive Steps

As we said, recursion can be seen as a reduction. We’ll take n and we’ll reduce it till we reach our base case, when n equals 0.

Our main problem is now: to sum a range of positive integers from n to 0. If n is equal to 5, it will be: 5 + 4 + 3 + 2 + 1 + 0 = 15.

How to decompose this problem to smallest instance of the same problem? A smaller sub-problem would be adding one number at a time instead of adding 5 different numbers.

We begin with n == 5. The first sub-problem would be adding a number. The solution is adding 4, which is (n - 1). Now, n == 4, and we can continue to apply this solution: adding (n - 1), which is 3. We apply this solution again and again, till we reach our base case, n == 0. That’s all. We’re done.

Now, you should be able to try to complete our recursiveSumRange function we began earlier.

function sumRange($n)
{
    if ($n == 0) {
        return $n;
    } else {
        return $n + sumRange($n - 1);
    }
}

echo sumRange(5);
; => 15

Calling sumRange again and again allows us to decrease n by 1 at each step, as we wanted.

Most of the time, people explain recursion by calling the same function repeatedly. Even if it’s partially true, we shouldn’t think about it that way.

What happens here is much more than repeating the call of a function. It’s more useful to think of it as a chain of deferred operations.

Let’s zoom on each return statement for each step of the recursive process:

  1. return 5 + sumRange(4)
  2. return 5 + 4 + sumRange(3)
  3. return 5 + 4 + 3 + sumRange(2)
  4. return 5 + 4 + 3 + 2 + sumRange(1)
  5. return 5 + 4 + 3 + 2 + 1 + sumRange(0)
  6. return 5 + 4 + 3 + 2 + 1 + 0
  7. 15

From the first step to the 5th, you can see an expansion. Every sum operation we need are chained. However, nothing is added at that point: that’s why we speak of deferred operations. They will be executed later.

At the 5th call of the sumRange function, we gives 0 as argument. It’s our base case! That’s why only 0 is returned from sumRange at the 6th step. It’s where the reduction happens. The function sumRange doesn’t call itself anymore so every summation operation can be executed.

These deferred operations are not visible in your code or in your output: they are in memory. The program needs to held them somehow, to be able to execute them at the end.

If we would had not specified the base case, the recursive process would never end. For example:

function sumRange($n)
{
    return $n + sumRange($n - 1);
}

echo sumRange(5);

What happens in that case?

  1. return 5 + sumRange(4)
  2. return 5 + 4 + sumRange(3)
  3. return 5 + 4 + 3 + sumRange(2)
  4. return 5 + 4 + 3 + 2 + sumRange(1)
  5. return 5 + 4 + 3 + 2 + 1 + sumRange(0)
  6. return 5 + 4 + 3 + 2 + 1 + 0 + sumRange(-1)
  7. return 5 + 4 + 3 + 2 + 1 + 0 + (-1) + sumRange(-2)

  1. return 5 + 4 + 3 + ... + sumRange(-488117)
  2. return 5 + 4 + 3 + ... + sumRange(-488118)

At that point, everything crash and I get a wonderful error message telling me that I blew up the memory allocation limit. As I was saying above, every deferred operation were held in memory, until there weren’t enough memory to contain them all!

Changing Words In a Sentence

Davina seems now full of hope to understand what recursion is. She never really thought about it that way. That’s great! Let’s continue our recursive exploration.

I’m sure you always wanted to change words in a sentence recursively. Personally, it’s my biggest dream.

The goal is to design a function which takes a string as argument, and recursively change some words. The words “i” and “me” will become “you”, the word “you” will become “i”.

We’ll only deal with lowercase sentences to make things easier.

For example, if you give the sentence i do not know if you hear me as argument of your recursive function, the output must be you do not know if i hear you.

To help you out, here are some helper functions you can use:

// Return first word of a string
function firstWord($sentence) {
    return explode(" ", $sentence)[0];
}

// Return every word of a string except the first
function butFirstWord($sentence) {
    return implode(" ", array_slice(explode(" ", $sentence), 1));
}

// Replace a word by another
function changeWord($word) {
    switch ($word) {
        case "i":
        case "me":
            return "you";
            break;
        case "you":
            return "i";
            break;
        default:
            return $word;
            break;
    }
}

You remind to Davina to find the best case first, and then the recursive steps.

If you have difficulties to find the recursive solution, you can try to solve the problem with an iterative loop first. I have faith in you. You can make it!

Here’s the recursive solution:

function replaceWords($sentence)
{
    if ($sentence == "") {
        return $sentence;
    } else {
        return changeWord(firstWord($sentence)) . " " . replaceWords(butFirstWord($sentence));
    }
}

This example is similar to the previous one. This time, we don’t have deferred summations, but deferred concatenations, while changing the words as we go.

First, we had to find the base case. What’s the value of $sentence which makes everything trivially simple? An empty sentence, which return an empty sentence. We write:

function replaceWords($sentence)
{
    if ($sentence == "") {
        return $sentence;
    }
}

Second, we need the recursive steps. Since the sentence can be of any length, it’s easier to consider one word at a time. Then, we can change it if we need to, before looking at the next word. If there is no word to change anymore, we reach our base case (the empty sentence).

That’s all! Who said that recursion was difficult?

Displaying a Directory Tree

At that point, Davina ask you this legitimate question: “What’s the point to know how to create recursive functions if we can use good old iterative loops instead? Can we use loops for my problem?”.

You look at Davina’s problem. You answer is clear: no, iteration would not be a good solution for that.

Davina is developing a backoffice for the content department of the e-commerce you’re working for. Copywriters want to be able to choose images of product from any sub-directory of a file system.

She needs to display a tree of directories which can have any depth, that is, an unknown number of nested sub-directories.

Thinking about the problem with Davina, you come up with a simple solution: having each directory and sub-directories as keys of a hashmap. You could then iterate through it and display each directory.

To make the problem more concrete and easier to reason about, you decide to create an arbitrary directory tree as follow:

dirs
├── 1
│   ├── 1-1
│   └── 1-2
│       ├── 1-2-1
│       └── 1-2-2
├── 2
│   ├── 2-1
│   │   ├── 2-1-1
│   │   │   ├── 2-1-1-1
│   │   │   └── 2-1-1-2
│   │   │       └── 2-1-1-2-1
│   │   └── 2-1-2
│   └── 2-2
└── 3
    └── 3-1

From there, you would like to populate a PHP array with the directory names as keys, nesting them to represent the depth of each sub-directory. Something like this:

Array
(
    [dirs] => Array (
        [1] => Array (
            [1-1] => Array ()
            [1-2] => Array (
                [1-2-1] => Array ()
                [1-2-2] => Array ()
            )
        )
        [2] => Array (
            [2-1] => Array (
                    [2-1-1] => Array (
                        [2-1-1-1] => Array ()
                        [2-1-1-2] => Array (
                            [2-1-1-2-1] => Array ()
                        )
                    )
                    [2-1-2] => Array ()
                )
            [2-2] => Array ()
        )
        [3] => Array (
            [3-1] => Array ()
        )
    )
)

First things first, we need to create these directories. Easy peasy! Open a terminal, go in your filesystem where the root directory should be, and run this one-liner:

mkdir -p dirs/{1/{1-1,1-2/{1-2-1,1-2-2}},2/{2-1/{2-1-1/{2-1-1-1,2-1-1-2/2-1-1-2-1},2-1-2},2-2},3/3-1}

To guarantee your success, you’ll need these functions, too. Copy them where you’ll implement your solution:

// Return all the direct children (sub-directories) of the $path
function subDirectories($path) {
    $files = array_diff(scandir($path), array('..', '.'));
    $directories = [];
    foreach ($files as $f) {
        if (is_dir(absolutePath($path, $f))) {
            $directories[] = $f;
        }
    }

    return $directories;
}

// Concatenate an absolute path with a directory
function absolutePath($path, $dir) {
    return $path . DIRECTORY_SEPARATOR . $dir;
}

We can first try to solve the problem using iterative loops. Remember, even if we defined an arbitrary example, the depth could go on an on.

Don’t spend too much time on your iteration. It might be possible using a stack, but I didn’t really try. You’ll see the likely failed attempt developers will usually do below:

// Iterative approach
function displayDirectories($path, $subDirs) {
    $totalDir = [];

    $absPath = dirPath($path, $subDirs);
    foreach (subDirectories($path) as $sub) {
        $totalDir[$sub] = [];
        $subPath = dirPath($path, $sub);

        foreach(subDirectories($subPath) as $subSub) {
            $totalDir[$sub][$subSub] = [];
            $subPath = dirPath($path, $subSub);

            // foreach...
        }

    }

    return $totalDir;
}

print_r(displayDirectories(".", "dirs"));
// => Return only two levels of sub-directories

This solution would works if we knew the depth of the directory tree. Since we don’t, we don’t know either how deep we need to loop.

The Base case

Let’s write the base case. What would be the easiest case we could solve?

function recursiveDisplayDirectories($path, $subDirs) {
    if (empty($subDirs)) {
        return $subDirs;
    }
}

print_r(recursiveDisplayDirectories(".", "dirs"));

The easiest case to solve would be a directory without any sub-directory.

The Recursive Steps

To understand better the problem, the structure of a filesystem can be displayed as a tree data structure. Recursion is very well suited to parse this kind of data structure.

Here it is:

The dotted lines are there to remind us that there could be even more sub-directories going down.

Now that we have a clear visual representation of our problem, let’s decompose it even more:

  1. Every directory has potentially an unlimited amount of direct children.
    • For example, the directory dirs has three direct sub-directories, 1,2,3, but it could have more.
  2. The depth of sub-directories, from one node to a leaf node (a node without any child) is unknown.
    • For example, the path from the node dirs to the node 2-1-1-2-1 could be even deeper. There could be another subdirectory 2-1-1-2-1-1, for example.

The first point can be easily solved. I gave you the function subDirectories($path) which will give you every direct children of a directory. We can use an iterative loop to parse them.

The second point is more tricky to solve. This is where we need recursion. We could imagine going deeper and deeper in the tree for each sub-directory using recursion, until we reach a leaf node, which is as well our base case.

We could create an array for each level we go through, and when we reach a leaf-node, we nest every array we created into each other.

I think we have now all the information we need to implement the recursive steps.

Don’t worry if it feels too difficult for you or Davina. It can be tricky to think in term of recursion when you’re used to write iterations. Focus, give it a good try, and even if you don’t succeed, it will bring you closer to the breakthrough you’ll have one day. I promise.

 1 2// recursive approach
 3function recursiveDisplayDirectories($path, $subDirs) {
 4    $total = [];
 5    if (empty($subDirs)) {
 6        return $subDirs;
 7    }
 8
 9    foreach($subDirs as $d) {
10        $newPath = dirPath($path, $d);
11        $total[$d] = recursiveDisplayDirectories($newPath, subDirectories($newPath));
12    }
13
14    return $total;
15}
16
17print_r(recursiveDisplayDirectories(".", ["dirs"]));

What’s happening here?

  1. Line 4: we create a new array. It will be the array for this level of directories, including every sub-directory.
  2. Line 5 to 7: we have our base case.
  3. Line 9: we begin to iterate through every sub-directory of the current directory.
  4. Line 11: the recursive process begins. For each sub-directory, we go a level deeper by updating the $path, and give as argument the sub-directories of the new path.
  5. Line 5: When we reached our base case for every branch of the tree, we return an empty array. Every array created during the expansion will be returned too, nesting one into another.
  6. Line 14: Finally, we return our nested array after going through every branch of the tree.

Here’s an optional exercise: what about implementing recursiveDisplayDirectories using only recursion, without the foreach?

Задача

Необходимо перетасовать элементы массива в случайном порядке.

Решение

Если у вас запущена версия PHP 4.3 или выше, то используйте функцию shuffle():

shuffle($array);

С более ранними версиями используйте функцию pc_array_shuffle(), показанную ниже:

Пример pc_array_shuffle()

function pc_array_shuffle($array) {
     $i = count($array);

while( -- $i) {
          $j = mt_rand(0, $i);

if ($i != $j) {
// перестановка элементов
     $tmp = $array[$j];
     $array[$j] = $array[$i];
     $array[$i] = $tmp;
     }
}
     return $array;
}

Ниже приведены примеры:

$cards = range(1,52);    // deal out 52 "cards"
$cards = pc_array_shuffle($cards);