#php #regex #arrays #sorting #loops
#php #регулярное выражение #массивы #сортировка #циклы
Вопрос:
Примите во внимание следующий код:
$files = array('1.js', '1.css', '2.js', '2.css', '3.js', '3.png');
$extensions = array();
foreach ($files as $file)
{
$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
if (empty($extensions[$extension]) === true)
{
$extensions[$extension] = 0;
}
$extensions[$extension];
}
arsort($extensions); // array('js' => 3, 'css' => 2, 'png' => 1)
$common_extension = key($extensions); // js
Код, похоже, работает так, как я хочу (мне все еще нужно проверить, что произойдет в случае ничьей, но это не имеет отношения к данному вопросу). Я ищу более эффективный (и компактный способ) перезаписи приведенного выше фрагмента, ближайший, который у меня пока есть, это:
$files = array('1.js', '1.css', '2.js', '2.css', '3.js', '3.png');
$extensions = array_count_values(array_map('strtolower', preg_replace('~^.*[.](.*)$~', '$1', $files)));
arsort($extensions, SORT_NUMERIC);
$common_extension = key($extensions);
Но это перебирает массив 3 раза, и это preg_replace()
не является пуленепробиваемым… Есть идеи?
Комментарии:
1. Я бы использовал обычные строковые функции вместо регулярного выражения, если вас это беспокоит. Недостатком является то, что в итоге вы получите еще пару строк, поскольку вы не можете передавать массивы в эти строковые функции.
2. Вы можете написать свой собственный метод, который содержит
strtolower
и вашpathinfo
фрагмент для использования вarray_map
вызове. Это сэкономит вам одну итерацию. И если вы не хотитеarray_count_values
запускать массив во второй раз, вам снова придется рассчитывать на себя.
Ответ №1:
Я бы сделал что-то вроде этого:
<?php
function getCommon($array, $result = array()) {
foreach ($array as $k => $v) { $array[$k] = strtolower(pathinfo($v, PATHINFO_EXTENSION)); }
$ext = array_count_values($array); arsort($ext,SORT_NUMERIC);
$k = array_keys($ext); $k0 = $k[0];
if ($ext[$k0] > $ext[$k[1]]) { $result[] = $k0; }
else { foreach ($ext as $k => $v) { if ($v == $ext[$k0]) { $result[] = $k; } } }
return $result;
}
$files = array('1.js', '2.js', '3.png', '4.css');
print_R($files);
print_R(getCommon($files));
$files2 = array('1.js', '2.js', '3.png', '4.png', '5.css');
print_R($files2);
print_R(getCommon($files2));
?>
Это вернет массив, значения которого будут общими расширениями, даже если их много.
Примечание: Не используйте regex, когда ваша работа может выполняться базовыми функциями PHP — regex слишком ресурсоемкий по сравнению со встроенными функциями php.
Комментарии:
1. Кажется, это делает именно то, что делает мой первый фрагмент, но вы перебираете массив 4 раза, я не получаю улучшения.
2. Он также возвращает draws, но не использует regex и он короче.
Ответ №2:
Я думал об этом вопросе уже довольно давно, и я думаю, что ваш первый фрагмент в значительной степени является ответом на него. Не похоже, что более короткий код — это более быстрый код. Этот код довольно быстрый и линейно масштабируется для больших массивов. Это в значительной степени сложность O (n) плюс алгоритм сортировки arsort
(я понятия не имею, какой метод он использует, но я ожидаю, что он будет быстрее, чем самописный). Единственное, что я могу предложить, это эту маленькую функцию, содержащую ваши фрагменты и небольшую настройку на случай, если расширения нет в массиве.
function count_ext($array){
$ret = array();
foreach($array as $ext){
$ext = strtolower(pathinfo($ext, PATHINFO_EXTENSION));
if( !isset($ret[$ext]) ) $ret[$ext] = 0;
$ret[$ext] ;
}
arsort($ret);
return $ret;
}
Я не знаю, сколько элементов в ваших массивах и насколько критичен ко времени ваш вариант использования, но этот метод подойдет хорошо.