Эквивалентен glob (), который будет работать с массивом вместо файловой системы - PullRequest
2 голосов
/ 09 декабря 2010

Первоначальным заголовком был алгоритм для вывода списка каталогов / файлов только по определенному пути объекта (S3, Google Storage)

https://gist.github.com/9a353e1589ff3ce84c02

Может ли кто-нибудь предложить алгоритм для вывода списка каталогов / файлов только в пределах определенного пути объекта? Например, ahostel.lt/img/ должен содержать только директории languages and psd, and files background.png, [..]. Мой алгоритм длинный и использует три цикла foreach, что плохо сказывается на производительности, но, возможно, у кого-то есть идея, как этого добиться с помощью регулярных выражений или других альтернатив.

Моя система работает на PHP, но общий логарифм вполне подойдет, если возможно преобразовать его в PHP.

Другими словами, я ищу алгоритм, подобный glob (), который работал бы с массивом вместо файловой системы.

Упрощенный список каталогов: https://gist.github.com/d0c3fa12d4b894938ba5

Ответы [ 4 ]

4 голосов
/ 09 декабря 2010

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

// Matches only immediate files of ahostel.lt/img/
$pattern = '#^ahostel\.lt/img/[^/]+\.[^/]+$#D';
$keys    = preg_grep($pattern, array_keys($array));
$items   = array_intersect_key($array, array_flip($keys));

Другой способ, поскольку итераторы - это круто без написания сделанного на заказ будет использовать RegexIterator для фильтрации ключей.Затем вы просто зациклились на отфильтрованном итераторе или использовали iterator_to_array(), чтобы получить массив, содержащий только отфильтрованные значения.

$items = new RegexIterator(
    new ArrayIterator($array),
    '#^ahostel\.lt/img/[^/]+\.[^/]+$#D',
    RegexIterator::MATCH,
    RegexIterator::USE_KEY
);

Существует несколько разных способов использованияили создайте итерационную фильтрацию, даже используя что-то вроде fnmatch() в методе accept() FilterIterator, чтобы использовать шаблоны с подстановочными знаками, такие как glob().

class GlobKeyFilterIterator extends FilterIterator
{
    protected $pattern;
    public function __construct(Iterator $it, $pattern)
    {
        $this->pattern = $pattern;
        parent::__construct($it);
    }
    public function accept()
    {
        return fnmatch($this->pattern, $this->key(), FNM_PATHNAME);
    }
}

$items = new GlobKeyFilterIterator(
    new ArrayIterator($array),
    'ahostel.lt/img/*.*'
);
1 голос
/ 11 декабря 2010

Многие программисты php имеют тенденцию усложнять вещи.Простая проблема всегда имеет простое решение.

$result = array();
foreach($dir as $k => $v)
    if(strpos($k, 'ahostel.lt/img/') === 0)
        $result[$k] = $v;

Мало того, что это более читабельно, чем любой сложный код smart-alec, но и намного быстрее.

1 голос
/ 09 декабря 2010

Другими словами, я ищу такой алгоритм, как glob (), который бы работал с массивом вместо файловой системы.

Вы можете использовать ArrayIterator и обернуть его в пользовательский FilterIterator

class CustomFilterIterator extends FilterIterator
{
    public function accept()
    {
        return strpos($this->key(), 'ahostel.lt/img/') === 0 &&
           pathinfo($this->key(), PATHINFO_EXTENSION) === 'png';
    }
}

Метод accept должен возвращать логическое значение. Если логическое значение равно TRUE, текущий итеративный элемент будет рассматриваться для включения в итерацию. В приведенном выше примере все, что не начинается с 'ahostel.lt/img/' и заканчивается расширением png, будет игнорироваться. Вы можете добавить дополнительные критерии фильтра по своему усмотрению. Для доступа к ключу вы используете $this->key(). Для значения используйте $this->current().

Использование ( кодовая панель )

$iterator = new CustomFilterIterator(new ArrayIterator($yourArray));

// to create a subset of the original array use
$filteredArray = iterator_to_array($iterator);

// or use good old foreach
foreach ($iterator as $path => $fileProperties) {
    var_dump($path, $fileProperties);
}

В качестве альтернативы или дополнения вы можете использовать RegexIterator .

Два основных преимущества при использовании итераторов - это повторное использование и тестируемость: итераторы могут быть сложены, поэтому вышеприведенный CustomFilterIterator может быть разбит на два итератора, например PathFilter и ExtensionFilter. Затем вам нужно просто обернуть ArrayIterator в оба итератора фильтра, чтобы создать гибкую цепочку фильтров сверху. Поскольку итераторы являются классами, их легко можно протестировать и смоделировать в классах, в которых итератор является зависимостью, чего нельзя делать при помещении логики фильтрации в цикл foreach.

Дополнительные ресурсы об итераторах и SPL в целом:

0 голосов
/ 09 декабря 2010
$already_included       = array();

    foreach($list as $key => $object)
    {
        $clean_key  = substr($key, strlen($uri));
        $explode    = explode('/', $clean_key);

        if(count($explode) >= 1 && !in_array($explode[0], $already_included))
        {
            $already_included[] = $explode[0];

            $files['directories'][] = array
            (
                'path'          => $uri . $explode[0] . '/',
                'name'          => $explode[0],
                'last_modified' => $object['last_modified'],
            );

        }

        if(substr_count($key, '/', $path_str_length) === 0)
        {
            $basename   = pathinfo($key, PATHINFO_BASENAME);

            if(strpos($basename, '.') !== FALSE)
            {
                $files['files'][]   = array
                (
                    'path'          => $key,
                    'name'          => $basename,
                    'size'          => $object['size'],
                    'last_modified' => $object['last_modified'],
                );
            }
            elseif(strrpos($basename, '_$folder$') !== FALSE)
            {
                $files['directories'][] = array
                (
                    'path'          => $key,
                    'name'          => substr($basename, 0, -9),
                    'last_modified' => $object['last_modified'],
                );
            }

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