我有一个有趣的问题......我正在构建一个API,用户通过字符串指定数组中某个元素的位置。像这样:
$path = "group1.group2.group3.element";
鉴于此字符串,我必须将一些值保存到数组中的正确位置。对于上面的例子,这将是:
$results['group1']['group2']['group3']['element'] = $value;
当然,对于$path
用户抛出的任何内容,代码都必须是通用的。
你会如何解决这个问题?
更新 - 解决方案:使用ern0(类似于我自己)和nikc的答案作为灵感,这是我决定采用的解决方案:
// returns reference to node in $collection as referenced by $path. For example:
// $node =& findnode('dir.subdir', $some_array);
// In this case, $node points to $some_array['dir']['subdir'].
// If you wish to create the node if it doesn't exist, set $force to true
// (otherwise it throws exception if the node is not found)
function &findnode($path, &$collection, $force = false)
{
$parts = explode('.', $path);
$where = &$collection;
foreach ($parts as $part)
{
if (!isset($where[$part]))
{
if ($force)
$where[$part] = array();
else
throw new RuntimeException('path not found');
}
$where =& $where[$part];
}
return $where;
}
$results = array();
$value = '1';
try {
$bucket =& findnode("group1.group2.group3.element", $results, true);
} catch (Exception $e) {
// no such path and $force was false
}
$bucket = $value; // read or write value here
var_dump($results);
谢谢大家的答案,这是一个很好的练习! :)
答案 0 :(得分:2)
也许,我不太了解PHP,但我找不到一个语言元素,它可以将元素插入到任何深层的数组中。
快速而肮脏的解决方案是 eval(),但正如我们所知,它是邪恶的。但是如果你正在观察输入(虚线形式)和结果(数组索引)超过10秒,你会问:为什么我们想要构建自定义深度数组,无论如何,因为它只需要两个简单* str_replace()* s将输入转换为结果。
编辑:这是eval版本,不要使用它:
$x = str_replace(".","][",$path);
$x = '$result[' . $x . '] = "' . $value . '";';
eval($x);
另一种方法是使用间接深入树深处而不知道它的深度:
$path = "group1.group2.group3.element";
$value = 55;
$x = explode(".",$path);
$result = Array();
$last = &$result;
foreach ($x as $elem) {
$last[$elem] = Array();
$last = &$last[$elem];
}
$last = $value;
echo("<pre>$path=$value\n");
print_r($result);
收集数组元素引用以便以后完成是一个非常有用的PHP功能。
答案 1 :(得分:2)
让我在混音中抛出我自己的答案::)
$path = "group1.group2.group3.element";
$results = array();
$parts = explode('.', $path);
$where = &$results;
foreach ($parts as $part)
{
$where =& $where[$part];
}
$where = $value;
答案 2 :(得分:1)
我认为这不是最好的,但我试图找出解决方案作为锻炼给自己:)
$path = "group1.group2.group3.element"; //path
$value = 2; //value
$results = array(); //an array
$t = explode(".",$path); //explode the path into an array
$n=count($t); //number of items
$i=0; //a counter variable
$r = &$results; //create the reference to the array
foreach($t as $p) //loop through each item
{
if($i == $n-1) //if it reached the last element, then insert the value
{
$r[$p] = $value;
break;
}
else //otherwise create the sub arrays
{
$r[$p] = array();
$r = &$r[$p];
$i++;
}
}
print_r($results); //output the structure of array to verify it
echo "<br>Value is: " . $results['group1']['group2']['group3']['element']; //output the value to check
希望它也会在你身边起作用.. :)
答案 3 :(得分:1)
当我评论你自己的答案时,你走在了正确的轨道上。事实上非常接近。我更喜欢使用递归,但这只是一个偏好,这也可以在一个线性循环中完成。
要找到一个节点(读取),这有效:
function &findnode(array $path, &$collection) {
$node = array_shift($path);
if (array_key_exists($node, $collection)) {
if (count($path) === 0) {
// When we are at the end of the path, we return the node
return $collection[$node];
} else {
// Otherwise, we descend a level further
return findnode($path, $collection[$node]);
}
}
throw new RuntimeException('path not found');
}
$collection = array(
'foo' => array(
'bar' => array(
'baz' => 'leafnode @ foo.bar.baz'
)
)
);
$path = 'foo.bar.baz';
$node =& findnode(explode('.', $path), $collection);
var_dump($node); // Output: 'leafnode @ foo.bar.baz'
要注入节点(写入),我们需要稍微修改逻辑以创建路径。
function &findnode(array $path, &$collection, $create = false) {
$node = array_shift($path);
// If create is set and the node is missing, we create it
if ($create === true && ! array_key_exists($node, $collection)) {
$collection[$node] = array();
}
if (array_key_exists($node, $collection)) {
if (count($path) === 0) {
// When we are at the end of the path, we return the node
return $collection[$node];
} else {
// Otherwise, we descend a level further
return findnode($path, $collection[$node], $create);
}
}
throw new RuntimeException('path not found');
}
$collection = array(
'foo' => array(
'bar' => array(
'baz' => 'leafnode @ foo.bar.baz'
)
)
);
$path = explode('.', 'baz.bar.foo');
$leaf = array_pop($path); // Store the leaf node
// Write
$node =& findnode($path, $collection, true);
$node[$leaf] = 'foo.bar.baz injected';
var_dump($collection); // Will have the new branch 'baz.bar.foo' with the injected value at the leaf
为了使这一切变得美观,你可以将读写操作包装在自己的函数中。更有可能所有这一切都在自己的班级里面。
因此,使用上述版本的findnode
,我们可以使用这两个函数来读取和写入您的集合数组。
function read($path, $collection) {
$path = explode('.', $path);
$val =& findnode($path, $collection);
return $val;
}
function write($value, $path, $collection) {
$path = explode('.', $path);
$leaf = array_pop($path);
$node =& findnode($path, $collection, true);
$node[$leaf] = $value;
}
NB!这不是一个完整的解决方案或最优雅。但是你可以自己弄清楚其余部分。
答案 4 :(得分:0)
我希望下面的代码可以正常工作,
$path = "group1.group2.group3.element";
$tempArr = explode('.', $path);
$results = array();
$arrStr = '$results';
$value = 'testing';
foreach( $tempArr as $ky=>$val) {
$arrStr .= "['".$val."']";
( $ky == count($tempArr) - 1 ) ? $arrStr .= ' = $value;' : '';
}
eval($arrStr);
print_r($results);