#php #arrays #algorithm
#php #массивы #алгоритм
Вопрос:
У меня есть класс, который хранит данные для хранения / доступа к данным с помощью ключей words.separated.by.dots, и он ведет себя следующим образом:
$object = new MyArray()
$object->setParam('user.name','marcelo');
$object->setParam('user.email','some@email.com');
$object->getParams();
/*
array(
'user' => array(
'name' => 'marcelo',
'email' => 'some@email.com'
)
);
*/
Он работает, но метод unsetParam() был ужасно реализован. Это произошло потому, что я не знал, как добиться этого без функции eval (). Хотя он работает, я обнаружил, что это действительно сложный алгоритм и что вам может показаться забавным пытаться достичь этого без eval().
class MyArray {
/**
* @param string $key
* @return Mura_Session_Abstract
*/
public function unsetParam($key)
{
$params = $this->getParams();
$tmp = $params;
$keys = explode('.', $key);
foreach ($keys as $key) {
if (!isset($tmp[$key])) {
return $this;
}
$tmp = $tmp[$key];
}
// bad code!
$eval = "unset($params['" . implode("']['", $keys) . "']);";
eval($eval);
$this->setParams($params);
return $this;
}
}
Метод тестирования:
public function testCanUnsetNestedParam()
{
$params = array(
'1' => array(
'1' => array(
'1' => array(
'1' => 'one',
'2' => 'two',
'3' => 'three',
),
'2' => array(
'1' => 'one',
'2' => 'two',
'3' => 'three',
),
)
),
'2' => 'something'
);
$session = $this->newSession();
$session->setParams($params);
unset($params['1']['1']['1']);
$session->unsetParam('1.1.1');
$this->assertEquals($params, $session->getParams());
$this->assertEquals($params['1']['1']['2'], $session->getParam('1.1.2'));
}
Ответ №1:
Это все?
<?php
$params = array(
'1' => array(
'1' => array(
'1' => array(
'1' => 'one',
'2' => 'two',
'3' => 'three',
),
'2' => array(
'1' => 'one',
'2' => 'two',
'3' => 'three',
),
)
),
'2' => 'something'
);
function unsetParam( amp;$array, $paramString ) {
$cur =amp; $array;
$splitted = explode( ".", $paramString );
$len = count( $splitted ) - 1;
for( $i = 0; $i < $len; $i ) {
if( isset( $cur[ $splitted[ $i ] ] ) ) {
$cur =amp; $cur[ $splitted[ $i ] ];
}
else {
return false;
}
}
unset( $cur[ $splitted[$i] ] );
}
unsetParam( $params, "1.1.1");
print_r( $params );
/*
Array
(
[1] => Array
(
[1] => Array
(
[2] => Array
(
[1] => one
[2] => two
[3] => three
)
)
)
[2] => something
)
*/
Комментарии:
1. творческое использование ссылок 🙂
2. Очень приятно! Большое вам спасибо!
Ответ №2:
Вы могли бы упростить свой код, если бы в вашем getParams
методе вы разбивали только на многомерный массив:
class MyArray {
private $params = array();
public function setParam($key, $value) {
$this->params[$key] = $value;
}
/**
* @param string $key
* @return Mura_Session_Abstract
*/
public function unsetParam($key)
{
unset($this->params[$key]);
return $this;
}
public function getParams() {
$retval = array();
foreach ($this->params as $key => $value) {
$aux = amp;$retval;
foreach (explode(".", $key) as $subkey) {
if (!isset($aux[$subkey])) $aux[$subkey] = array();
$aux = amp;$aux[$subkey];
}
$aux = $value;
}
return $retval;
}
}
Комментарии:
1. Это, безусловно, лучший ответ, имхо; ключи уникальны, так почему многомерный массив вообще полезен? Просто сохраните / извлеките с помощью string.with. точки, если только нет какой-то убедительной детали, о которой мы еще не слышали?
2. Многомерный массив лучше соответствует моим потребностям. Мой класс был очень похож на приведенный выше, и в итоге я изменился.
3. @Kato, давайте доведем ваш аргумент до логического завершения: зачем вообще нужен весь этот класс? 😀
4. @Kato это моя точка зрения. Он беспокоился о внутреннем поведении, когда это не имеет значения. Только
getParams
это имело бы некоторое значение в возвращаемом значении…5. @Esailija Похоже, что это класс конфигурации. Для меня он хочет сделать что-то вроде шаблона реестра…
Ответ №3:
@gustavotkg и @Esailija оба предложили несколько отличных идей. Вот еще один простой, понятный и краткий подход, который позволяет вообще избежать функции unset() (которая в некоторых случаях может стать причудливой).
Это, конечно, было бы наиболее полезно, когда $ params ограничен значениями, составляющими менее, скажем, 1-10 кб (что начинает становиться немного дороже в отделе процессора / памяти):
<?php
$params = array(
'1' => array(
'1' => array(
'1' => array(
'1' => 'one-one',
'2' => 'one-two',
'3' => 'one-three',
),
'2' => array(
'1' => 'two-one',
'2' => 'two-two',
'3' => 'two-three',
),
)
),
'2' => 'something'
);
function filterParams($params, $refKey, $base = '') {
$newvals = array();
foreach($params as $k=>$v) {
$joinedKey = $base? $base . '.' . $k : $k;
if( $joinedKey != $refKey ) {
$newvals[$k] = is_array($v)? filterParams($v, $refKey, $joinedKey) : $v;
}
}
return $newvals;
}
var_dump(filterParams($params, '1.1.2'));