用字符串定义数组中的位置

时间:2011-12-21 07:23:02

标签: php arrays

我有一个有趣的问题......我正在构建一个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);

谢谢大家的答案,这是一个很好的练习! :)

5 个答案:

答案 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);