Skip to content

Commit

Permalink
Fix sorting of rows with different length using custom itemgetter
Browse files Browse the repository at this point in the history
  • Loading branch information
arturponinski committed Feb 4, 2022
1 parent 1ed38a6 commit bf79ae4
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 3 deletions.
6 changes: 6 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Changes
=======

Version 1.7.8
-------------

* Fix sorting of rows with different length
By :user:`arturponinski`, :issue:`385`.

Version 1.7.7
-------------

Expand Down
24 changes: 22 additions & 2 deletions petl/comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


import operator

from functools import partial

from petl.compat import text_type, binary_type, numeric_types

Expand Down Expand Up @@ -104,6 +104,26 @@ def _typestr(x):


def comparable_itemgetter(*args):
f = operator.itemgetter(*args)
getter = operator.itemgetter(*args)
getter_with_default = _itemgetter_with_default(*args)

def f(x):
try:
return getter(x)
except (IndexError, KeyError):
return getter_with_default(x)
g = lambda x: Comparable(f(x))
return g


def _itemgetter_with_default(*args):
""" itemgetter compatible with `operator.itemgetter` behavior, filling missing
values with default instead of raising IndexError or KeyError """
def get_default(obj, item, default):
try:
return obj[item]
except (IndexError, KeyError):
return default
if len(args) == 1:
return partial(get_default, item=args[0], default=None)
return lambda obj: tuple(get_default(obj, item=item, default=None) for item in args)
19 changes: 19 additions & 0 deletions petl/test/transform/test_sorts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import pytest

import petl
from petl.compat import next


Expand Down Expand Up @@ -522,3 +523,21 @@ def test_issorted():
assert not issorted(table5, key='foo')
assert issorted(table5, key='foo', reverse=True)
assert not issorted(table5, key='foo', reverse=True, strict=True)


def test_sort_missing_cell_numeric():
""" Sorting table with missing values raises IndexError #385 """
tbl = (('a', 'b'), ('4',), ('2', '1'), ('1',))
expect = (('a', 'b'), ('1',), ('2', '1'), ('4',))

sorted = sort(tbl)
ieq(expect, sorted)


def test_sort_missing_cell_text():
""" Sorting table with missing values raises IndexError #385 """
tbl = (('a', 'b', 'c'), ('C',), ('A', '4', '5'))
expect = (('a', 'b', 'c'), ('A', '4', '5'), ('C',))

sorted = sort(tbl)
ieq(expect, sorted)
1 change: 0 additions & 1 deletion petl/transform/sorts.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,6 @@ def _iternocache(self, source, key, reverse):
else:
indices = range(len(hdr))
# now use field indices to construct a _getkey function
# TODO check if this raises an exception on short rows
getkey = comparable_itemgetter(*indices)

# TODO support native comparison
Expand Down

0 comments on commit bf79ae4

Please sign in to comment.