Reputation: 45
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?
Upvotes: 3
Views: 63
Reputation: 324750
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
Upvotes: 3
Reputation: 1013
More complicated, but algorithm could look like that:
"{Variant #"
with unique placeholder, i.e. "%line" . $i
where $i
is auto-increment, and store removed part in temporary array. It could be done by preg_replace_callback()
methodpreg_replace_callback()
.Upvotes: 0