Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion rebulk/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,10 @@ def _fix_matches_offset(chain_part_matches: Iterable[Match], input_string: str,
@staticmethod
def _group_by_match_index(matches: Iterable[Match]) -> dict[int, list[Match]]:
grouped_matches_dict: dict[int, list[Match]] = {}
for match_index, match in itertools.groupby(matches, lambda m: m.match_index):
# groupby only groups consecutive equal keys, so sort by match_index first
# to avoid splitting (and overwriting) groups when matches are unordered.
sorted_matches = sorted(matches, key=lambda m: m.match_index)
for match_index, match in itertools.groupby(sorted_matches, lambda m: m.match_index):
grouped_matches_dict[match_index] = list(match)
return grouped_matches_dict

Expand Down
22 changes: 21 additions & 1 deletion rebulk/test/test_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
from rebulk.pattern import FunctionalPattern, RePattern, StringPattern

from ..chain import Chain
from ..match import Match
from ..rebulk import Rebulk
from ..validators import chars_surround

if TYPE_CHECKING:
from ..match import Match, Matches
from ..match import Matches


def test_chain_close() -> None:
Expand Down Expand Up @@ -489,3 +490,22 @@ def chain_breaker(matches: Matches) -> bool:
matches[0].value = 1
matches[1].value = 2
matches[2].value = 3


def test_group_by_match_index_sorts_unordered_input() -> None:
# groupby only groups consecutive equal keys, so _group_by_match_index must
# sort by match_index first; otherwise unordered matches split (and overwrite)
# groups sharing an index.
matches: list[Match] = []
for start, match_index in ((0, 2), (5, 0), (10, 1), (15, 2)):
match = Match(start, start + 1, name="test")
match.match_index = match_index
matches.append(match)

grouped = Chain._group_by_match_index(matches)

assert sorted(grouped) == [0, 1, 2]
assert [m.start for m in grouped[0]] == [5]
assert [m.start for m in grouped[1]] == [10]
# both match_index=2 matches are kept, in their original relative order
assert [m.start for m in grouped[2]] == [0, 15]
Loading