Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reordering string's lines using php regexp

I need to reorder lines in the string using php regexp. But I don't know how to tell php to not change the same line twice. Let me explain.

Input string is:

$comment = "
some text

{Varinat #3 smth}
{Varinat #4 smth else}
{Varinat #1 smth else 1}
some another text
{Varinat #2 smth else 2}
{Varinat #5 smth else 5}
";

I need to order variants:

$comment = "
some text

{Varinat #1 smth else 1}
{Varinat #2 smth else 2}
{Varinat #3 smth}
some another text
{Varinat #4 smth else}
{Varinat #5 smth else 5}    
";

I've got code:

$variants = [
    3 => 1,
    4 => 2,
    1 => 3,
    2 => 4,
    5 => 5,
];

$replacements = [];
foreach ($variants as $key => $variant) {
    $replacements['/{Varinat #'.$variant.'\ /is'] = '{Varinat #'.$key . ' ';
}


$comment = preg_replace(array_keys($replacements), array_values($replacements), $comment);

echo $comment;

But it does exta changes:

some text

{Varinat #1 smth}
{Varinat #2 smth else}
{Varinat #1 smth else 1}
some another text
{Varinat #2 smth else 2}
{Varinat #5 smth else 5}

As you can see lines 1 & 2 are doubled. It happens because php does change: 3->1 and then 1->3.

I have only uggly solution: change lines as

3 => 1*,
4 => 2*,
1 => 3*,
2 => 4*,
5 => 5*,

and then remove *

Is there more elegant solution?

like image 259
Ivan Levantsov Avatar asked Apr 29 '26 09:04

Ivan Levantsov


1 Answers

Why not build an algorithm to actually do what you want to do, that is to sort the {Varinat lines?

$lines = explode("\n",$comment); // assuming $comment is your input here
$numbered_lines = array_map(null,$lines,range(1,count($lines)));
usort($numbered_lines,function($a,$b) {
    if( preg_match('(^\{Varinat #(\d+))', $a[0], $match_a)
     && preg_match('(^\{Varinat #(\d+))', $b[0], $match_b)) {
        return $match_a[1] - $match_b[1]; // sort based on variant number
    }
    return $a[1] - $b[1]; // sort based on line number
});
$sorted_lines = array_column($numbered_lines,0);
$result = implode("\n",$sorted_lines);

For some reason the code above does not work in PHP 7. Here's an alternative that does.

$lines = explode("\n",$comment);
$processed = array_map(function($line) {
    if( preg_match('(^\{Varinat #(\d+))', $line, $match)) {
        return [$line,$match[1]];
    }
    return [$line,null];
}, $lines);
$variant = array_filter($processed,function($data) {return $data[1];});
usort($variant,function($a,$b) {return $a[1] - $b[1];});
$sorted = array_map(function($data) use (&$variant) {
    if( $data[1]) return array_shift($variant)[0];
    else return $data[0];
},$processed);
$result = implode("\n",$sorted);

This works by first tagging each line with its "variant" number, if it has one. Then it filters the list down to just those lines and sorts them. Finally it loops over all lines once more, and either keeps it as is (if it isn't a variant) or replaces it with the next sorted variant line.

> Demo on 3v4l

like image 77
Niet the Dark Absol Avatar answered May 02 '26 04:05

Niet the Dark Absol



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!