Поиск в несимметричном многомерном массиве с PHP - PullRequest
0 голосов
/ 04 августа 2020

Я нашел много вопросов и несколько отличных ответов, чтобы искать в массиве с PHP. Но каждый раз сценарий слишком точно отвечал на вопрос, а не глобально ИЛИ все массивы симметричны.

Мой массив может выглядеть так:

$data = [
    'steve' => [
        'id'     => [
            '#text' => 1,
        ],
        'pseudo' => [
            '#text' => 'LOL'
        ],
    ],
    'albert' => [
        'id'     => [
            '#text' => 2,
        ],
        'pseudo' => [
            '#text' => 'KILLER'
        ],
    ],
    'john' => [
        'id'     => [
            '#text' => 3,
        ],
        'pseudo' => [
            '#text' => 'NOOBS'
        ],
    ],
];

Это означает, что мой массив может выглядеть красиво симметрично сгенерированный или совершенно беспорядочный и со случайным подмассивом.

Моя цель - поискать внутри и найти PSEUDO идентификатора AN. И, к сожалению, я не могу изменить веб-сервис, который дает мне этот результат.

Я пробовал использовать array_column или array_search, но единственное, что я успешно вернул, - это обнаружил он что-то или нет. Но не могу его идентифицировать. И мой скрипт был очень медленным.

Что-то вроде:

search($array, 2); //which return KILLER

или может быть больше?

Мой массив может иметь много идентификаторов (100+). Вот и пытаюсь найти что-нибудь оптимизировать. : /

Ответы [ 2 ]

0 голосов
/ 04 августа 2020

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

Он использует предопределенные итераторы:

в сочетании с настраиваемым итератором PathAsKeyDecorator.

Демо здесь

<?php
declare(strict_types=1);

final class PathAsKeyDecorator implements \Iterator
{
    private RecursiveIteratorIterator $inner;

    public function __construct(RecursiveIteratorIterator $inner)
    {
        $this->inner = $inner;
    }

    public function current()
    {
        return $this->inner->current();
    }

    public function next(): void
    {
        $this->inner->next();
    }

    public function key()
    {
        $path = [];
        for ($i = 0, $depth = $this->inner->getDepth(); $i <= $depth; $i++) {
            $path[] = $this->inner->getSubIterator($i)->key();
        }

        return $path;
    }

    public function valid(): bool
    {
        return $this->inner->valid();
    }

    public function rewind(): void
    {
        $this->inner->rewind();
    }
}

$input = [
    'steve'  => [
        'id' => [
            '#text' => 1,
        ],
    ],
    'albert' => [
        'id' => [
            '#text' => 2,
        ],
    ],
    'john'   => [
        'profil' => [
            'id' => [
                '#text' => 3,
            ],
        ],
    ],
];

// this is the filter function that should be customized given your requirements
// or create a factory function which produces these types of filter functions
$filter = static function ($current, array $path): bool {
    // with help from the PathAsKeyDecorator
    // we can decide on the path to the current value
    return ['id', '#text'] === array_slice($path, -2)
        // and the current value
        && 2 === $current;
};


// configure the iterator
$it = new CallbackFilterIterator(
    new PathAsKeyDecorator(new RecursiveIteratorIterator(new RecursiveArrayIterator($input))),
    $filter,
);

// traverse the iterator
foreach ($it as $path => $val) {
    print_r([
        'path' => $path,
        'val'  => $val
    ]);
}

Альтернативным подходом может быть метод с простыми функциями, например:

Демо здесь

<?php
declare(strict_types=1);

function iterateWithPath(iterable $input, array $path = []): iterable {
    foreach ($input as $key => $value) {
        $pathToHere = [...$path, $key];

        is_iterable($value)
            ? yield from iterateWithPath($value, $pathToHere)
            : yield $pathToHere => $value;
    }
}

function filterIterable(iterable $it, callable $filter): iterable {
    foreach ($it as $key => $value) {
        if ($filter($value, $key)) {
            yield $key => $value;
        }
    }
}

$input = [
    'steve'  => [
        'id' => [
            '#text' => 1,
        ],
    ],
    'albert' => [
        'id' => [
            '#text' => 2,
        ],
    ],
    'john'   => [
        'profil' => [
            'id' => [
                '#text' => 3,
            ],
        ],
    ],
];

$it = filterIterable(iterateWithPath($input), static function ($current, array $path): bool {
    return ['id', '#text'] === array_slice($path, -2)
        && 2 === $current;
});

foreach ($it as $path => $val) {
    print_r([
        'path' => $path,
        'val' => $val
    ]);
}
0 голосов
/ 04 августа 2020

полное редактирование причины изменения структуры массива. json_encode используется для преобразования массива в строку. Выполняет свою работу только тогда, когда в каждом срезе массива есть идентификатор и псевдо.

$data = [
    'steve' => [
        'id'     => [
            '#text' => 1,
        ],
        'pseudo' => [
            '#text' => 'LOL'
        ],
    ],
    'albert' => [
        'id'     => [
            '#text' => 2,
        ],
        'pseudo' => [
            '#text' => 'KILLER'
        ],
    ],
    'john' => [
        'id'     => [
            '#text' => 3,
        ],
        'pseudo' => [
            '#text' => 'NOOBS'
        ],
    ],
];


$data = json_encode($data, JSON_NUMERIC_CHECK);
preg_match_all('~"id":{"#text":([^{]*)}~i', $data, $ids);
preg_match_all('~"pseudo":{"#text":"([^{]*)"}~i', $data, $pseudos);

$lnCounter = 0;
$laResult  = array();
foreach($ids[1] as $lnId) {
    $laResult[$lnId] = $pseudos[1][$lnCounter];
    $lnCounter++;
}

echo $laResult[2];

приводит к KILLER

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...