Home > Blockchain >  Moving parts of {token} to the end of line based on condition
Moving parts of {token} to the end of line based on condition

Time:04-04

From a multiline input I want to move tokens if they're inside {} and match some number. Sample input

# (811) (1485) [2756] {29} [555] {15} 
# (811) (1476) {20} {15} (1485) [196] [2441] 
# (911) (619) {19} (1476) [2765] [2752] {21}

From the above line, I want to move tokens if it's not {19} or {20} to the end of line.
Sample output

# (811) (1485) [2756] [555] {15} {29} 
# (811) (1476) {20} (1485) [196] [2441] {15} 
# (911) (619) {19} (1476) [2765] [2752] {21}

I can do a preg match with preg_match_all("/\{\d \}/", $input, $matches); but then what to do?

CodePudding user response:

You can gather all the matches for {19} and {20} per line in an array while filtering the splitted string, and then joining them back together.

Code example

foreach (explode("\n", $str) as $str) {
    $result = array_reduce(explode(" ", $str), function($acc, $curr) {
        preg_match("/{(?!19|20)\d }/", $curr) ? $acc['move'][] = $curr :  $acc['valid'][] = $curr;
        return $acc;
    }, ['valid' => [], 'move' => []]);
    echo implode(" ", array_merge($result['valid'], array_reverse($result['move']))) . PHP_EOL;
}

Output

# (811) (1485) [2756] [555] {15} {29}
# (811) (1476) {20} (1485) [196] [2441] {15}
# (911) (619) {19} (1476) [2765] [2752] {21}

About the code

Te code first splits the string on newlines, because the moving parts are per line.

Then you can use for example explode to split the line on a space and use array_reduce to inspect the separate parts.

You can initialise array reduce with an array that contains 2 arrays ['valid' => [], 'move' => []]

In the callback function, the accumulator $acc then already contains that array, which you can then populate with the difference in matches by using the array key like $acc['valid']

The pattern {(?!19|20)\d } matches { and then asserts that it is not directly followed by either 19} or 20} If that is the case, it matches 1 or more digits between curly braces.

To get a result with just single spaces between the "words" you can merge both arrays, and then use implode on a space.

See a php demo.

CodePudding user response:

This solution allows for multiple token punctuations. In this example all tokens starting with '{' or '(' will be moved to the end:

$input = <<< STRING
# (811) (1485) [2756] {29} [555] {15}
# (811) (1476) {20} {15} (1485) [196] [2441]
# (911) (619) {19} (1476) [2765] [2752] {21}
STRING;

$excluded = [ '{19}', '{20}', '(811)' ];

$startPunctuations = array_unique(array_map(fn($exclude) => $exclude[0], $excluded));

$result = implode(
  "\n",
  array_map(
    fn($line): string => implode(
      ' ',
      array_map(
        fn($element) => implode(' ', $element),
        array_reduce(
          explode(' ', $line),
          fn($carry, $item) => in_array($item[0], $startPunctuations) && !in_array($item, $excluded)
            ? [ $carry[0], [ ...$carry[1], $item ] ]
            : [ [ ...$carry[0], $item ], $carry[1] ],
          [ [], [] ]
        )
      )
    ),
    explode("\n", $input)
  )
);

echo $result;

// # (811) [2756] [555] (1485) {29} {15}
// # (811) {20} [196] [2441] (1476) {15} (1485)
// # {19} [2765] [2752] (911) (619) (1476) {21}
  • Related