вопрос по массивам
getVal($array, "Config/DB/Hostname");
Функция должна обратиться к элементу $array["Config"]["DB"]["Hostname"] и возвратить полученное значение. Длина пути должна быть произвольная.
Это реализуемо?
foreach($path_array as $path_element)
$array=$array[$path_element];
return $array;
Как то я совсем спросонья повис над задачей.
Спасибо за пример. +1 в карму. :)
{
$path_array = explode('/',$path);
for($i=0; $i<sizeof($path_array); $i++) $array = $array[$path_array[$i]];
return $array;
}
Не уверен что explode и создание нового массива быстрее чем foreach.
$path=str_replace("/","\"][\"",$path);
eval("$ret=$array[\"".$path."\"]");
return $ret;
хз на сколько быстро это будет.
В первом приведенном примере и foreach и explode и создание массива
foreach медленный? если не ошибаюсь он наиболее быстрый из циклов.
Вы ошибаетесь
может быть
$arr = array();
for($i = 0; $i < 300000; $i++) $arr[] = rand();
$time_start1 = microtime(1);
$n = sizeof($arr);
for($i = 0; $i < $n; $i++) {
$a = $arr[$i];
}
$time_end1 = microtime(1);
$time1 = round($time_end1 - $time_start1, 4);
$i = 0;
$time_start2 = microtime(1);
$n = sizeof($arr);
while($i < $n) {
$a = $arr[$i];
$i++;
}
$time_end2 = microtime(1);
$time2 = round($time_end2 - $time_start2, 4);
$time_start3 = microtime(1);
foreach($arr as $key => $val) {
$a = $val;
}
$time_end3 = microtime(1);
$time3 = round($time_end3 - $time_start3, 4);
echo "time1: $time1 s\n";
echo "time2: $time2 s\n";
echo "time3: $time3 s\n";
?>
что в этом коде не так?
* Gets a value from an array using a dot separated path.
*
* // Get the value of $array['foo']['bar']
* $value = Arr::path($array, 'foo.bar');
*
* Using a wildcard "*" will search intermediate arrays and return an array.
*
* // Get the values of "color" in theme
* $colors = Arr::path($array, 'theme.*.color');
*
* // Using an array of keys
* $colors = Arr::path($array, array('theme', '*', 'color'));
*
* @param array array to search
* @param mixed key path string (delimiter separated) or array of keys
* @param mixed default value if the path is not set
* @param string key path delimiter
* @return mixed
*/
public static function path($array, $path, $default = NULL, $delimiter = NULL)
{
if ( ! Arr::is_array($array))
{
// This is not an array!
return $default;
}
if (is_array($path))
{
// The path has already been separated into keys
$keys = $path;
}
else
{
if (array_key_exists($path, $array))
{
// No need to do extra processing
return $array[$path];
}
if ($delimiter === NULL)
{
// Use the default delimiter
$delimiter = Arr::$delimiter;
}
// Remove starting delimiters and spaces
$path = ltrim($path, "{$delimiter} ");
// Remove ending delimiters, spaces, and wildcards
$path = rtrim($path, "{$delimiter} *");
// Split the keys by delimiter
$keys = explode($delimiter, $path);
}
do
{
$key = array_shift($keys);
if (ctype_digit($key))
{
// Make the key an integer
$key = (int) $key;
}
if (isset($array[$key]))
{
if ($keys)
{
if (Arr::is_array($array[$key]))
{
// Dig down into the next part of the path
$array = $array[$key];
}
else
{
// Unable to dig deeper
break;
}
}
else
{
// Found the path requested
return $array[$key];
}
}
elseif ($key === '*')
{
// Handle wildcards
$values = array();
foreach ($array as $arr)
{
if ($value = Arr::path($arr, implode('.', $keys)))
{
$values[] = $value;
}
}
if ($values)
{
// Found the values requested
return $values;
}
else
{
// Unable to dig deeper
break;
}
}
else
{
// Unable to dig deeper
break;
}
}
while ($keys);
// Unable to find the value requested
return $default;
}
а по поводу скорости foreach и вообще скорости всего: http://www.phpbench.com/
ну и на stackoverflow можно ещё поискать
кстати, похоже, что это быстрейшее решение (особенно если исключить всякие проверки), так как в отличие от решений выше результат функции explode не проходится никакими foreach'ами, а получается через array_shift().. это явно быстрее, правда приводит к уничтожению массива.. но по сути он в конце и не нужен
и главное по ссылке этой функции исходный массив не передавать, а то она его тоже затрёт (строка 75)
А как дела обстоят с eval? Быстрый ли он?
Вы тоже ошибаетесь. Принципиально. Что вернёт ваша функция, если её вызвать со следующими параметрами:
echo getVal($array, 'a/b/c/');
?
Не бойтесь, shift не применяется к массиву, в котором ищутся элементы. Он применяется к сгенерированному (временному) массиву ключей, которые используются для поиска.
При использовании shift я не смогу установить значение в исходный массив. В этом и вся загвоздка.
По вашему в реестре я собираюсь хранить только конфиг? Было бы смешно только ради этого так изголяться.
В реестр будут вноситься абсолютно все полученные и сгенерированные данные. Я лишь хочу сделать хранилище данных иерархичным, самый быстрый и удобный способ - это многомерный массив.
onerror,
Реализация с foreach,for мне понятна. Она хороша. Меня интересует, будет ли с eval работать быстрее.
{
$path_array = explode('/',$path);
for($i=0; $i<sizeof($path_array); $i++)
$array =& $array[$path_array[$i]];
$array = $new_value;
}
$rr = array('a' => array('b' => array('c' => 'd')));
setVal($rr, 'a/b/c', 'e');
print_r($rr);
shift применяется к массиву, созданному из строки вашего поиска, то есть к 'aaaa/bbb/vard', как справедливо заметил onerror.. вам никто не мешает в кохановской функции в else написать не break, а установку значения у этого ключа (строка 115)))
и вообще в той же kohana есть и установка значений по ключу:
* Set a value on an array by path.
*
* @see Arr::path()
* @param array $array Array to update
* @param string $path Path
* @param mixed $value Value to set
* @param string $delimiter Path delimiter
*/
public static function set_path( & $array, $path, $value, $delimiter = NULL)
{
if ( ! $delimiter)
{
// Use the default delimiter
$delimiter = Arr::$delimiter;
}
// Split the keys by delimiter
$keys = explode($delimiter, $path);
// Set current $array to inner-most array path
while (count($keys) > 1)
{
$key = array_shift($keys);
if (ctype_digit($key))
{
// Make the key an integer
$key = (int) $key;
}
if ( ! isset($array[$key]))
{
$array[$key] = array();
}
$array = & $array[$key];
}
// Set key on inner-most array
$array[array_shift($keys)] = $value;
}
Неа по скорости отработки будет всё равно что просто выполнить строку $ret=$array[......]. А вто str_replace хз.
я бы сказал, что в этом коде "не так" последние три строки :-). Лучше бы так:
echo "while: $time2 s\n";
echo "foreach: $time3 s\n</pre>";
echo "while: $time2 s\n";
echo "foreach: $time3 s\n</pre>";
:facepalm: ты еще скажи, что ты даный код в браузере заупскал. хотя судя по всему именно там.. :facepalm:
{
$path_array = explode('/',$path);
for($i=0; $i<sizeof($path_array); $i++) $array = $array[$path_array[$i]];
return $array;
}
=) А постоянно вычислять размер массива который в процессе цикла не меняется, увеличивает производительность?)))
Фанат! Молодец. Я готов тебе рассказать как правильно строить храмы в мою честь и уже написал 10 заповедей. Будешь моим пророком, сынок
Специально для тебя рассказываю - записывай: нужно перед циклом создать переменную куда сохранишь размер массива. Писал я это в запаре после 30 часов без сна, опечатался. Молодец что заметил!
Вы еще меня научите как пользоваться echo.
OFFTOPIC DETECTED:
Я тебя троллю, а ты поддаешься ;) Больше не буду. Давай завязывать с конфронтациями
class Registry
{
private $Data;
private function parsePath($path)
{
$_path = explode("/", $path);
$path_s = "";
if(!$_path)
{
foreach($_path as $sub_path)
{
$path_s .= "[" . $sub_path . "]";
}
}
return $path_s;
}
public function getVal($path)
{
$_path = "Data" . $this->parsePath($path);
print $this->$_path;
}
public function setVal($path, $value)
{
$_path = "Data" . $this->parsePath($path);
$this->$_path = $value;
}
public function addPath($path, $new_path)
{
$_path = "Data" . $this->parsePath($path);
$this->$_path = $new_path;
}
public function delPath($path)
{
$_path = "Data" . $this->parsePath($path);
unset($this->$_path);
}
public function dumpPath($path)
{
$_path = "Data" . $this->parsePath($path);
print "<pre>" . var_export($this->$_path) . "</pre>";
}
}
?>
$r->setVal('a/b', 'c');
$r->setVal('c/d', 'E');
$r->getVal('a/b'); //=>E !!
Делайте уже "как во втором посте" :-)
$r->setVal('a/b', 'c');
$r->setVal('c/d', 'E');
$r->getVal('a/b'); //=>E !!
Делайте уже "как во втором посте" :-)
Тоже задумывался об этом. Как-то некрасиво и несерьезно выглядит вариант с переменными переменных.