快排實現仿order by多欄位排序

曾春雲發表於2019-02-15
class OrderBy(object):

    def __init__(self, sequence, *condition, **extra_condition):
        """
        排序初始化條件
        condition為優先排序條件,序列內元素必須為字典型別
        extra_condition為額外的條件因素,當condition不存在時,額外條件才會生效
        :param sequence:
        :param condition:
        :param extra_condition:
        """
        self.array = sequence
        self.condition = list(condition)
        self.extra_condition = extra_condition
        self.has_condition = bool(self.condition)

    def partition(self, left, right, key=None, desc=False, func=None):
        """
        將區間資料進行排序
        分割槽比較,選擇一個基準,根據排序規則,正序:比基準大的放右邊,比基準小的放左邊
        :param left: 序列區間的開始位置
        :param right: 序列區間的結束位置
        :param key: 對於字典物件,如果指定key,則以key物件比較否則當None值處理
        :param desc: 比較規則為正序或倒序
        :param func: 對值進行處理特殊處理的函式
        :return:
        """
        pivot = self.array[left]
        if isinstance(pivot, dict):
            if key is not None:
                pivot = pivot.get(key)
        if callable(func):
            pivot = func(pivot)
        low = left
        while low < right:
            while low < right:
                _left = self.array[low]
                _right = self.array[right]
                if isinstance(_left, dict):
                    _left = _left.get(key)
                if isinstance(_right, dict):
                    _right = _right.get(key)
                if callable(func):
                    _left = func(_left)
                    _right = func(_right)
                if desc:
                    # 倒序,右邊值與基準值都不為空,且右邊值小於基準值,左移
                    if _right and pivot and _right < pivot:
                        right -= 1
                    # 倒序,右邊值為空,左移
                    elif not _right:
                        right -= 1
                    # 倒序,左邊值與基準值都不為空,且左邊值大於等於基準值,右移
                    elif _left and pivot and _left >= pivot:
                        low += 1
                    # 倒序,基準值為空,左邊值不為空,右移
                    elif _left and not pivot:
                        low += 1
                    else:
                        break
                else:
                    # 正序,基準為空,右邊值不為空,左移
                    if _right and not pivot:
                        right -= 1
                    # 正序,右邊值與基準都不為空,且右邊值大於基準值,左移
                    elif _right and pivot and _right > pivot:
                        right -= 1
                    # 正序,左邊值與基準都不為空,且左邊值小於等於基準值,右移
                    elif _left and pivot and _left <= pivot:
                        low += 1
                    # 正序,左邊值為空,右移
                    elif not _left:
                        low += 1
                    else:
                        break
            if low < right:
                temp = self.array[low]
                self.array[low] = self.array[right]
                self.array[right] = temp
        self.array[left], self.array[low] = self.array[low], self.array[left]
        return low

    def quick_sort(self, left=0, right=None, key=None, desc=False, func=None):
        """
        快速排序演算法
        :param left: 區間起始位置
        :param right: 區間結束位置
        :param key: 字典元素使用指定鍵名值排序
        :param desc: 是否倒序
        :param func: 對於排序值,支援使用函式處理
        :return:
        """
        if right is None:
            right = len(self.array) - 1
        if left < right:
            pivot_position = self.partition(left, right, key, desc, func)
            self.quick_sort(left, pivot_position - 1, key, desc, func)
            self.quick_sort(pivot_position + 1, right, key, desc, func)

    def sort(self, **condition):
        if self.has_condition:
            if not self.condition:
                return
            _condition = self.condition.pop(0)
            if isinstance(_condition, dict):
                condition[`key`] = _condition.get(`key`)
                condition[`desc`] = _condition.get(`desc`, False)
        else:
            condition.update(**self.extra_condition)
        left = condition.get(`left`)
        right = condition.get(`right`)
        if not left:
            left = 0
            condition[`left`] = left
        if not right:
            right = len(self.array) - 1
            condition[`right`] = right
        self.quick_sort(**condition)
        self.sub_sort(left, right, condition.get(`key`))

    def sub_sort(self, left, right, key=None, next_index=0):
        """
        標記當前位置begin,及下一個位置end
        當begin位置的元素與end位置元素不等時,當begin!=end - 1時,則進行區間排序
        :param left: 區間起始位置
        :param right: 區間結束位置
        :param key: 當前排序鍵名
        :param next_index: 下一個條件位置
        :return:
        """
        condition_size = len(self.condition)
        if not condition_size > next_index:
            return
        begin = left
        for end in range(left, right + 1):
            _left = self.array[begin]
            _right = self.array[end]
            if isinstance(_left, dict):
                _left = _left.get(key)
            if isinstance(_right, dict):
                _right = _right.get(key)
            # 當上一個值與當前值不相等,則進入二次排序
            # 當上一個值與當前值相等,且當前位置等於邊界位置,且還有下一個排序條件,則進入二次排序
            if _left != _right or (
                    end == right and condition_size >= next_index + 1):
                condition = self.condition[next_index]
                _key = condition.get(`key`)
                desc = condition.get(`desc`)
                func = condition.get(`func`)
                if end == right:
                    _end = end
                else:
                    _end = end - 1
                self.quick_sort(begin, _end, _key, desc, func)
                self.sub_sort(begin, end, _key, next_index + 1)
                begin = end


if __name__ == `__main__`:
    a = [dict(age=18, money=200, name=`z1`),
         dict(age=16, money=200, name=`z2`),
         dict(age=16, money=200, name=`z3`),
         dict(age=16, money=100, name=`z4`),
         dict(age=16, money=200, name=`z5`)]
    order_by = OrderBy(a, dict(key=`age`, desc=False),
                       dict(key=`money`, desc=True),
                       dict(key=`name`, desc=True))
    print(a)
    order_by.sort()
    print(a)

相關文章