LREM:从列表中移除指定元素

用户可以通过 LREM 命令移除列表中的指定元素:

LREM list count element

count 参数的值决定了 LREM 命令移除元素的方式:

  • 如果 count 参数的值等于 0,那么 LREM 命令将移除列表中包含的所有指定元素。

  • 如果 count 参数的值大于 0,那么 LREM 命令将从列表的左端开始向右进行检查,并移除最先发现的 count 个指定元素。

  • 如果 count 参数的值小于 0,那么 LREM 命令将从列表的右端开始向左进行检查,并移除最先发现的 abs(count)个指定元素(abs(count)即 count 的绝对值)。

LREM 命令在执行完毕之后将返回被移除的元素数量作为命令的返回值。

举个例子,对于以下 3 个包含相同元素的列表来说:

redis> RPUSH sample1 "a" "b" "b" "a" "c" "c" "a"
(integer) 7
redis> RPUSH sample2 "a" "b" "b" "a" "c" "c" "a"
(integer) 7
redis> RPUSH sample3 "a" "b" "b" "a" "c" "c" "a"
(integer) 7

执行以下命令将移除 sample1 列表包含的所有 "a" 元素:

redis> LREM sample1 0 "a"
(integer) 3 -- 移除了3个"a"元素
redis> LRANGE sample1 0 -1
1) "b" -- 列表里面已经不再包含"a"元素
2) "b"
3) "c"
4) "c"

而执行以下命令将移除 sample2 列表最靠近列表左端的 2 个 "a" 元素:

redis> LREM sample2 2 "a"
(integer) 2 -- 移除了2个"a"元素
redis> LRANGE sample2 0 -1
1) "b"
2) "b"
3) "c"
4) "c"
5) "a"

因为上面的 LREM 命令只要求移除最先发现的 2 个 "a" 元素,所以位于列表最右端的 "a" 元素并没有被移除,图4-23 展示了这个 LREM 命令的执行过程。

image 2025 01 03 17 00 14 590
Figure 1. 图4-23 从 sample2 列表中移除最靠近列表左端的 2 个 "a" 元素

最后,执行以下命令将移除 sample3 列表最靠近列表右端的 2 个 "a" 元素:

redis> LREM sample3 -2 "a"
(integer) 2 -- 移除了2个"a"元素
redis> LRANGE sample3 0 -1
1) "a"
2) "b"
3) "b"
4) "c"
5) "c"

因为上面的 LREM 调用只要求移除最先发现的 2 个 "a" 元素,所以位于列表最左端的 "a" 元素并没有被移除,图4-24 展示了这个 LREM 调用的执行过程。

image 2025 01 03 17 01 39 250
Figure 2. 图4-24 从 sample3 列表中移除最靠近列表右端的 2 个 "a" 元素

其他信息

  • 复杂度:O(N),其中 N 为给定列表的长度。

  • 版本要求:LREM 命令从 Redis 1.0.0 版本开始可用。

示例:待办事项列表

现在很多人都会使用待办事项软件(也就是通常说的 TODO 软件)来管理日常工作,这些软件通常会提供一些列表,用户可以将要做的事情记录在待办事项列表中,并将已经完成的事项放入已完成事项列表中。图4-25 就展示了一个使用待办事项软件记录日常生活事项的例子。

image 2025 01 03 17 03 21 986
Figure 3. 图4-25 使用待办事项软件记录日常生活事项

代码清单4-3 展示了一个使用列表实现的待办事项程序,这个程序的核心概念是使用两个列表来分别记录待办事项和已完成事项:

  • 当用户添加一个新的待办事项时,程序就把这个事项放入待办事项列表中。

  • 当用户完成待办事项列表中的某个事项时,程序就把这个事项从待办事项列表中移除,并放入已完成事项列表中。

代码清单4-3 代码事项程序:/list/todo_list.py
def make_todo_list_key(user_id):
    """
    储存待办事项的列表。
    """
    return user_id + "::todo_list"

def make_done_list_key(user_id):
    """
    储存已完成事项的列表。
    """
    return user_id + "::done_list"


class TodoList:

    def __init__(self, client, user_id):
        self.client = client
        self.user_id = user_id
        self.todo_list = make_todo_list_key(self.user_id)
        self.done_list = make_done_list_key(self.user_id)

    def add(self, event):
        """
        将指定事项添加到待办事项列表中。
        """
        self.client.lpush(self.todo_list, event)

    def remove(self, event):
        """
        从待办事项列表中移除指定的事项。
        """
        self.client.lrem(self.todo_list, 0, event)

    def done(self, event):
        """
        将待办事项列表中的指定事项移动到已完成事项列表,
        以此来表示该事项已完成。
        """
        # 从待办事项列表中移除指定事项
        self.remove(event)
        # 并将它添加到已完成事项列表中
        self.client.lpush(self.done_list, event)

    def show_todo_list(self):
        """
        列出所有待办事项。
        """
        return self.client.lrange(self.todo_list, 0, -1)

    def show_done_list(self):
        """
        列出所有已完成事项。
        """
        return self.client.lrange(self.done_list, 0, -1)

done() 方法是 TodoList 程序的核心,它首先会使用 LREM 命令从代办事项列表中移除指定的事项,然后再将该事项添加到已完成事项列表中,使得该事项可以在代办事项列表中消失,转而出现在已完成列表中。

作为例子,我们可以通过执行以下代码,创建出一个 TODO 列表对象:

>>> from redis import Redis
>>> from todo_list import TodoList
>>> client = Redis(decode_responses=True)
>>> todo = TodoList(client, "peter's todo")

然后通过执行以下代码,向 TODO 列表中加入待完成事项:

>>> todo.add("go to sleep")
>>> todo.add("finish homework")
>>> todo.add("watch tv")
>>> todo.add("have lunch")
>>> todo.add("buy some milk")
>>> todo.show_todo_list()
['buy some milk', 'have lunch', 'watch tv', 'finish homework', 'go to sleep']

当完成某件事情之后,我们可以把它从待办事项列表移动到已完成事项列表:

>>> todo.done("buy some milk")
>>> todo.show_todo_list()
['have lunch', 'watch tv', 'finish homework', 'go to sleep']
>>> todo.show_done_list()
['buy some milk']

最后,如果我们不再需要去做某件事情,那么可以把它从待办事项列表中移除:

>>> todo.remove("watch tv")
>>> todo.show_todo_list()
['have lunch', 'finish homework', 'go to sleep']