Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP - Perform parsing rules on nested array

So I have a nested array, that mimics a table layout (columns and rows):

{
    "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"
        }
    ]
}

So "1", "2", "3" are the columns and then under each column, there can be any number of rows.

Now I am trying to do, so my users can perform various parsing rules on either:

  1. All columns and all rows.
  2. Specific columns and all rows.

Whenever a column / row has been parsed, it should be returned to the "original array".

For this, I have created a class that will apply the different parsing rules I have in specified. Getting the parsing rule works fine. I am currently stuck in the actual text transformation/parsing aspect.

Consider I have a parsing rule called "regexTextReplace", that looks like this:

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);
    }
}

This is how I'm using it:

$options = [
    'pattern' => '/Trevor/i',
    'replacement' => 'Oliver',
];
$engine = new regexTextReplace($options);
$columns = $engine->apply($document->content, 1); //"1" is the specific column.

$columns returns:

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

Two problems here:

  1. It successfully apply the parsing rule (Trever is replaced with Oliver). But it only returns the first column, but I want the entire original array to be transformed.
  2. If I remove the 1 from the apply() method, I get below error:
Array to string conversion

on below line:

return preg_replace($this->pattern, $this->replacement, $table);

Can anyone guide me in the right direction, so I can perform my parsing rule on any column or on all columns, and return the transformed data back to my original array?

like image 472
oliverbj Avatar asked May 16 '19 08:05

oliverbj


2 Answers

I would rewrite the apply function to loop over the entire table, processing each column if the column argument is not set, or if it matches the current table column:

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;
}

Demo on 3v4l.org

like image 176
Nick Avatar answered Oct 15 '22 15:10

Nick


You could rewrite your apply method to this:

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));
}

You can pass either a single column, or an array of columns, or nothing (false) to specify the columns you want adjusted.

Demo: https://3v4l.org/Kn4FY

like image 3
Jeto Avatar answered Oct 15 '22 16:10

Jeto