def order_by(collection, keys, orders=None, reverse=False): """This method is like :func:`sort_by` except that it sorts by key names instead of an iteratee function. Keys can be sorted in descending order by prepending a ``"-"`` to the key name (e.g. ``"name"`` would become ``"-name"``) or by passing a list of boolean sort options via `orders` where ``True`` is ascending and ``False`` is descending. Args: collection (list|dict): Collection to iterate over. keys (list): List of keys to sort by. By default, keys will be sorted in ascending order. To sort a key in descending order, prepend a ``"-"`` to the key name. For example, to sort the key value for ``"name"`` in descending order, use ``"-name"``. orders (list, optional): List of boolean sort orders to apply for each key. ``True`` corresponds to ascending order while ``False`` is descending. Defaults to ``None``. reverse (bool, optional): Whether to reverse the sort. Defaults to ``False``. Returns: list: Sorted list. Example: >>> items = [{'a': 2, 'b': 1}, {'a': 3, 'b': 2}, {'a': 1, 'b': 3}] >>> results = order_by(items, ['b', 'a']) >>> assert results == [{'a': 2, 'b': 1},\ {'a': 3, 'b': 2},\ {'a': 1, 'b': 3}] >>> results = order_by(items, ['a', 'b']) >>> assert results == [{'a': 1, 'b': 3},\ {'a': 2, 'b': 1},\ {'a': 3, 'b': 2}] >>> results = order_by(items, ['-a', 'b']) >>> assert results == [{'a': 3, 'b': 2},\ {'a': 2, 'b': 1},\ {'a': 1, 'b': 3}] >>> results = order_by(items, ['a', 'b'], [False, True]) >>> assert results == [{'a': 3, 'b': 2},\ {'a': 2, 'b': 1},\ {'a': 1, 'b': 3}] .. versionadded:: 3.0.0 .. versionchanged:: 3.2.0 Added `orders` argument. .. versionchanged:: 3.2.0 Added :func:`sort_by_order` as alias. .. versionchanged:: 4.0.0 Renamed from ``order_by`` to ``order_by`` and removed alias ``sort_by_order``. """ if isinstance(collection, dict): collection = collection.values() # Maintain backwards compatibility. if pyd.is_boolean(orders): reverse = orders orders = None comparers = [] if orders: for i, key in enumerate(keys): if pyd.has(orders, i): order = 1 if orders[i] else -1 else: order = 1 comparers.append((pyd.property_(key), order)) else: for key in keys: if key.startswith('-'): order = -1 key = key[1:] else: order = 1 comparers.append((pyd.property_(key), order)) def comparison(left, right): # pylint: disable=useless-else-on-loop,missing-docstring for func, mult in comparers: result = _cmp(func(left), func(right)) if result: return mult * result else: return 0 return sorted(collection, key=cmp_to_key(comparison), reverse=reverse)
def test_is_boolean(case, expected): assert _.is_boolean(case) == expected
def order_by(collection, keys, orders=None, reverse=False): """This method is like :func:`sort_by` except that it sorts by key names instead of an iteratee function. Keys can be sorted in descending order by prepending a ``"-"`` to the key name (e.g. ``"name"`` would become ``"-name"``) or by passing a list of boolean sort options via `orders` where ``True`` is ascending and ``False`` is descending. Args: collection (list|dict): Collection to iterate over. keys (list): List of keys to sort by. By default, keys will be sorted in ascending order. To sort a key in descending order, prepend a ``"-"`` to the key name. For example, to sort the key value for ``"name"`` in descending order, use ``"-name"``. orders (list, optional): List of boolean sort orders to apply for each key. ``True`` corresponds to ascending order while ``False`` is descending. Defaults to ``None``. reverse (bool, optional): Whether to reverse the sort. Defaults to ``False``. Returns: list: Sorted list. Example: >>> items = [{'a': 2, 'b': 1}, {'a': 3, 'b': 2}, {'a': 1, 'b': 3}] >>> results = order_by(items, ['b', 'a']) >>> assert results == [{'a': 2, 'b': 1},\ {'a': 3, 'b': 2},\ {'a': 1, 'b': 3}] >>> results = order_by(items, ['a', 'b']) >>> assert results == [{'a': 1, 'b': 3},\ {'a': 2, 'b': 1},\ {'a': 3, 'b': 2}] >>> results = order_by(items, ['-a', 'b']) >>> assert results == [{'a': 3, 'b': 2},\ {'a': 2, 'b': 1},\ {'a': 1, 'b': 3}] >>> results = order_by(items, ['a', 'b'], [False, True]) >>> assert results == [{'a': 3, 'b': 2},\ {'a': 2, 'b': 1},\ {'a': 1, 'b': 3}] .. versionadded:: 3.0.0 .. versionchanged:: 3.2.0 Added `orders` argument. .. versionchanged:: 3.2.0 Added :func:`sort_by_order` as alias. .. versionchanged:: 4.0.0 Renamed from ``order_by`` to ``order_by`` and removed alias ``sort_by_order``. """ if isinstance(collection, dict): collection = collection.values() # Maintain backwards compatibility. if pyd.is_boolean(orders): reverse = orders orders = None comparers = [] if orders: for i, key in enumerate(keys): if pyd.has(orders, i): order = 1 if orders[i] else -1 else: order = 1 comparers.append((pyd.property_(key), order)) else: for key in keys: if key.startswith('-'): order = -1 key = key[1:] else: order = 1 comparers.append((pyd.property_(key), order)) def comparison(left, right): # pylint: disable=useless-else-on-loop,missing-docstring for func, mult in comparers: result = _cmp(func(left), func(right)) if result: return mult * result else: return 0 return sorted(collection, key=cmp_to_key(comparison), reverse=reverse)