PHP - 对嵌套数组执行解析规则

12

我有一个嵌套数组,模拟表格布局(列和行):

{
    "1": [
        {
            "row": "My name is Trevor\n"
        },
        {
            "row": "Can you see me?\n"
        },
        {
            "row": "\f"
        }
    ],
    "2": [
        {
            "row": Hey there! Some other text.\n"
        },
        {
            "row": "What is up?\n"
        },
        {
            "row": "\f"
        }
    ],
    "3": [
        {
            "row": "Some text on the third column. First row."
        },
        {
            "row": "\f"
        }
    ]
}

所以,“1”,“2”,“3”是列,然后在每个列下面,可以有任意数量的行。
现在我正在尝试让我的用户对以下内容执行各种解析规则:
1. 所有列和所有行。 2. 特定列和所有行。
每当一个列/行被解析时,它应该返回到“原始数组”。
为此,我创建了一个类,将应用我指定的不同解析规则。获取解析规则运作良好。我目前卡在实际文本转换/解析方面。
考虑我有一个名为“regexTextReplace”的解析规则,看起来像这样:
class regexTextReplace
{
    private $pattern;
    private $replacement;

    public function __construct(array $arguments)
    {
        $this->pattern = $arguments['pattern'];
        $this->replacement = $arguments['replacement'];
    }

    public function apply(array $table, $column = false): array
    {
        $table = $column ? $table[$column] : $table;

        return array_map('self::regex_replace', $table);
    }

    public function regex_replace(array $table)
    {
        return preg_replace($this->pattern, $this->replacement, $table);
    }
}

这是我如何使用它的方式:
$options = [
    'pattern' => '/Trevor/i',
    'replacement' => 'Oliver',
];
$engine = new regexTextReplace($options);
$columns = $engine->apply($document->content, 1); //"1" is the specific column.

$columns 返回:

[
  {
    "row": "My name is Oliver\n"
  },
  {
    "row": "Can you see my?\n"
  },
  {
    "row": "\f"
  }
]

这里有两个问题:

  1. 它成功地应用了解析规则(将Trever替换为Oliver)。但它只返回第一列,而我希望整个原始数组都被转换。
  2. 如果我从apply()方法中删除1,则会出现以下错误:
Array to string conversion

在下面这一行:
return preg_replace($this->pattern, $this->replacement, $table);

有没有人能指导我正确的方向,以便我可以在任何列或所有列上执行我的解析规则,并将转换后的数据返回到我的原始数组中?


由于这看起来相当复杂:您是否尝试将TDD应用于此?例如,从编写最简单的测试用例开始,然后进行下一个稍微困难一点的用例?这将帮助您保持该类可维护性。 - Nico Haase
请查看您的条件:$table = $column ? $table[$column] : $table;column 参数为 false 值时,table 将作为数组出现,因此会出现“将数组转换为字符串”的错误。我宁愿使 table 的值保持一致,即始终为一个数组,而不是每次调用 apply() 时都进行循环。 - Oluwatobi Samuel Omisakin
3个回答

6
我会重写apply函数来循环遍历整个表格,在列参数未设置或匹配当前表格列时处理每一列:
public function apply(array $table, $column = false): array
{
    $out = array();
    foreach ($table as $col => $rows) {
        if ($column === false || $col == $column) {
            $out[$col] = array_map('self::regex_replace', $rows);
        }
        else {
            $out[$col] = $rows;
        }
    }
    return $out;
}

在 3v4l.org 上的示例演示


我该如何指定是解析特定列还是所有列?$engine->apply($document->content);将解析所有列。但是,如果我只想将规则应用于第一列,应该这样写:$engine->apply($document->content, 1); - oliverbj
1
@oliverbj,你说得完全正确。如果你看一下我的演示,你会发现我稍微修改了你的数据(在第二列的一行上添加了“Trevor”),这样你就可以看到单列版本和整个表格版本都可以工作。 - Nick
啊,抱歉,在你修改代码之前我看了一下。它现在运行得非常好。 - oliverbj
没关系。我真的很感激SO提供的所有帮助 :) 感谢每个人抽出时间回答我的问题 - 我已经卡在这里两天了! - oliverbj
1
如果它总是一个数组,只需将“$col == $column”替换为“in_array($col, $column)” - Nick
显示剩余2条评论

3
您可以将您的apply方法重写为以下方式:
public function apply(array $table, $columns = false): array
{
    $columns = $columns === false ? array_keys($table) : (array)$columns;

    return array_map(function ($column) use ($table, $columns) {
      return in_array($column, $columns) ? array_map('self::regex_replace', $table[$column]) : $table[$column];
    }, array_keys($table));
}

您可以传递单个列,也可以传递列的数组或 false(什么都不传)来指定您想要调整的列。

示例: https://3v4l.org/Kn4FY


但是 OP 的整个问题在于他想要返回 整个 数组,即使他只处理了一列... - Nick
@Nick 我刚刚意识到这点,哈哈。我会编辑我的回答的(不过你的看起来完全没问题)。 - Jeto
@oliverbj 根据Nick的建议修改了我的帖子。 - Jeto

2

Just loop it and do the regex on all subarrays:

$content = json_decode($json, true);

$options = [
    'pattern' => '/Trevor/i',
    'replacement' => 'Oliver',
];
$engine = new regexTextReplace($options);
foreach($content as $key => $v){
    $columns[$key] = $engine->apply($content, $key);
}
var_dump($columns);

工作演示:
https://3v4l.org/Pk2rC

在“PHP”中循环而不是在类中循环的好处在于,您仍然可以将正则表达式应用于其中一个或两个子数组。
如果在类中循环,则需要传递更多参数以限制循环或进行某种类型的数组切片。

最初的回答:

将循环放在“PHP”代码中而不是类中的好处在于,您仍然可以将正则表达式应用于其中一个或两个子阵列。如果您在类中循环,则需要传递更多参数来限制循环,或执行某种类型的数组切片。


那个循环可能应该放在类的“apply”函数中。 - Nick
谢谢Andreas! 如果我没记错的话,这个循环默认会查找所有列,对吗?因为$keyapply()函数的第二个参数。如果我在我的$options数组中添加了一个column => all,那么我能否将其传递进去呢?因为我希望用户可以解析所有列或仅特定列。 - oliverbj
@Nick 如果我直接将这个放在 apply() 方法中,那么我该如何在 foreach 中调用实际的 apply() 方法呢? - oliverbj
@oliverbj 如果你把循环放到apply函数里面,就不需要使用foreach了。 - Nick
@Andreas,在我的原始代码中,我有这个:$columns = $engine->apply($document->content, 1); //"1"是我想应用解析规则的列。其中,“1”是我想要应用解析规则的列。 - oliverbj
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接