or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bitmap-operations.mdcore-clients.mdgeneric-operations.mdgeospatial-operations.mdhash-operations.mdindex.mdlist-operations.mdlua-scripting.mdpubsub-operations.mdserver-management.mdserver-operations.mdset-operations.mdsorted-set-operations.mdstack-extensions.mdstream-operations.mdstring-operations.mdtransaction-operations.mdvalkey-support.md

list-operations.mddocs/

0

# List Operations

1

2

Redis list data type operations providing ordered collections of strings. Lists are implemented as linked lists, making them excellent for use cases requiring fast insertion and removal at the head and tail, such as queues, stacks, and activity feeds. Lists can hold up to 2^32 - 1 elements.

3

4

## Capabilities

5

6

### Basic List Operations

7

8

Core list manipulation functions for adding, removing, and accessing list elements.

9

10

```python { .api }

11

def lpush(self, name: KeyT, *values: EncodableT) -> ResponseT: ...

12

13

def rpush(self, name: KeyT, *values: EncodableT) -> ResponseT: ...

14

15

def lpushx(self, name: KeyT, *values: EncodableT) -> ResponseT: ...

16

17

def rpushx(self, name: KeyT, *values: EncodableT) -> ResponseT: ...

18

19

def lpop(self, name: KeyT, count: Optional[int] = None) -> Union[Optional[bytes], List[Optional[bytes]]]: ...

20

21

def rpop(self, name: KeyT, count: Optional[int] = None) -> Union[Optional[bytes], List[Optional[bytes]]]: ...

22

23

def llen(self, name: KeyT) -> ResponseT: ...

24

25

def lindex(self, name: KeyT, index: int) -> Optional[bytes]: ...

26

27

def lset(self, name: KeyT, index: int, value: EncodableT) -> ResponseT: ...

28

```

29

30

### List Range Operations

31

32

Functions for working with list ranges including retrieval, trimming, and range-based modifications.

33

34

```python { .api }

35

def lrange(self, name: KeyT, start: int, end: int) -> List[bytes]: ...

36

37

def ltrim(self, name: KeyT, start: int, end: int) -> ResponseT: ...

38

39

def lrem(self, name: KeyT, count: int, value: EncodableT) -> ResponseT: ...

40

41

def linsert(

42

self,

43

name: KeyT,

44

where: Literal["BEFORE", "AFTER"],

45

refvalue: EncodableT,

46

value: EncodableT

47

) -> ResponseT: ...

48

49

def lpos(

50

self,

51

name: KeyT,

52

value: EncodableT,

53

rank: Optional[int] = None,

54

count: Optional[int] = None,

55

maxlen: Optional[int] = None

56

) -> Union[Optional[int], List[Optional[int]]]: ...

57

```

58

59

### Blocking Operations

60

61

Blocking list operations that wait for elements to become available, useful for implementing queues and producer-consumer patterns.

62

63

```python { .api }

64

def blpop(

65

self,

66

keys: Union[KeyT, Iterable[KeyT]],

67

timeout: float = 0

68

) -> Optional[Tuple[bytes, bytes]]: ...

69

70

def brpop(

71

self,

72

keys: Union[KeyT, Iterable[KeyT]],

73

timeout: float = 0

74

) -> Optional[Tuple[bytes, bytes]]: ...

75

76

def brpoplpush(

77

self,

78

src: KeyT,

79

dst: KeyT,

80

timeout: float = 0

81

) -> Optional[bytes]: ...

82

83

def blmove(

84

self,

85

first_list: KeyT,

86

second_list: KeyT,

87

src: Literal["LEFT", "RIGHT"],

88

dest: Literal["LEFT", "RIGHT"],

89

timeout: float = 0

90

) -> Optional[bytes]: ...

91

```

92

93

### Atomic Move Operations

94

95

Functions for atomically moving elements between lists.

96

97

```python { .api }

98

def rpoplpush(self, src: KeyT, dst: KeyT) -> Optional[bytes]: ...

99

100

def lmove(

101

self,

102

first_list: KeyT,

103

second_list: KeyT,

104

src: Literal["LEFT", "RIGHT"],

105

dest: Literal["LEFT", "RIGHT"]

106

) -> Optional[bytes]: ...

107

```

108

109

## Usage Examples

110

111

### Basic List Operations

112

113

```python

114

import fakeredis

115

116

client = fakeredis.FakeRedis()

117

118

# Create a list by pushing elements

119

client.lpush('tasks', 'task1', 'task2', 'task3') # Left push (prepend)

120

client.rpush('tasks', 'task4', 'task5') # Right push (append)

121

122

# Get list length

123

length = client.llen('tasks')

124

print(f"List length: {length}") # 5

125

126

# Get entire list

127

all_tasks = client.lrange('tasks', 0, -1)

128

for task in all_tasks:

129

print(task.decode())

130

131

# Access specific elements

132

first_task = client.lindex('tasks', 0)

133

last_task = client.lindex('tasks', -1)

134

print(f"First: {first_task.decode()}, Last: {last_task.decode()}")

135

136

# Pop elements

137

left_popped = client.lpop('tasks') # Remove from left (head)

138

right_popped = client.rpop('tasks') # Remove from right (tail)

139

print(f"Popped: {left_popped.decode()}, {right_popped.decode()}")

140

```

141

142

### Queue Implementation (FIFO)

143

144

```python

145

import fakeredis

146

147

class Queue:

148

def __init__(self, client, name):

149

self.client = client

150

self.name = name

151

152

def enqueue(self, *items):

153

"""Add items to the end of the queue"""

154

return self.client.rpush(self.name, *items)

155

156

def dequeue(self):

157

"""Remove and return item from the front of the queue"""

158

item = self.client.lpop(self.name)

159

return item.decode() if item else None

160

161

def dequeue_blocking(self, timeout=0):

162

"""Block until an item is available to dequeue"""

163

result = self.client.blpop(self.name, timeout=timeout)

164

return result[1].decode() if result else None

165

166

def size(self):

167

return self.client.llen(self.name)

168

169

def peek(self, count=1):

170

"""Look at items without removing them"""

171

items = self.client.lrange(self.name, 0, count - 1)

172

return [item.decode() for item in items]

173

174

# Usage

175

client = fakeredis.FakeRedis()

176

queue = Queue(client, 'job_queue')

177

178

# Add jobs

179

queue.enqueue('process_image_1', 'send_email_2', 'backup_database')

180

181

# Process jobs

182

while queue.size() > 0:

183

job = queue.dequeue()

184

print(f"Processing: {job}")

185

```

186

187

### Stack Implementation (LIFO)

188

189

```python

190

import fakeredis

191

192

class Stack:

193

def __init__(self, client, name):

194

self.client = client

195

self.name = name

196

197

def push(self, *items):

198

"""Add items to the top of the stack"""

199

return self.client.lpush(self.name, *items)

200

201

def pop(self):

202

"""Remove and return item from the top of the stack"""

203

item = self.client.lpop(self.name)

204

return item.decode() if item else None

205

206

def peek(self):

207

"""Look at the top item without removing it"""

208

item = self.client.lindex(self.name, 0)

209

return item.decode() if item else None

210

211

def size(self):

212

return self.client.llen(self.name)

213

214

def is_empty(self):

215

return self.size() == 0

216

217

# Usage

218

client = fakeredis.FakeRedis()

219

stack = Stack(client, 'call_stack')

220

221

# Add function calls

222

stack.push('main()', 'process_data()', 'validate_input()')

223

224

# Process stack (LIFO)

225

while not stack.is_empty():

226

func = stack.pop()

227

print(f"Returning from: {func}")

228

```

229

230

### List Manipulation

231

232

```python

233

import fakeredis

234

235

client = fakeredis.FakeRedis()

236

237

# Initialize a list

238

client.rpush('numbers', '1', '2', '3', '2', '4', '2', '5')

239

240

# Find positions of elements

241

positions = client.lpos('numbers', '2', count=0) # Find all occurrences

242

print(f"Positions of '2': {positions}")

243

244

# Insert element before/after another element

245

client.linsert('numbers', 'BEFORE', '3', '2.5')

246

client.linsert('numbers', 'AFTER', '4', '4.5')

247

248

# Remove specific elements

249

removed_count = client.lrem('numbers', 2, '2') # Remove first 2 occurrences of '2'

250

print(f"Removed {removed_count} occurrences of '2'")

251

252

# Update element at specific index

253

client.lset('numbers', 0, '0') # Set first element to '0'

254

255

# Trim list to keep only a range

256

client.ltrim('numbers', 1, -2) # Keep elements from index 1 to second-to-last

257

258

# View final result

259

result = client.lrange('numbers', 0, -1)

260

print("Final list:", [item.decode() for item in result])

261

```

262

263

### Multiple List Operations

264

265

```python

266

import fakeredis

267

268

client = fakeredis.FakeRedis()

269

270

# Pop from multiple lists (first available)

271

client.rpush('list1', 'a', 'b', 'c')

272

client.rpush('list2', 'x', 'y', 'z')

273

274

result = client.blpop(['list1', 'list2'], timeout=1)

275

if result:

276

list_name, value = result

277

print(f"Popped '{value.decode()}' from {list_name.decode()}")

278

279

# Move elements between lists atomically

280

client.rpush('source', 'item1', 'item2', 'item3')

281

client.lpush('destination', 'existing')

282

283

# Move from right of source to left of destination

284

moved_item = client.rpoplpush('source', 'destination')

285

print(f"Moved item: {moved_item.decode()}")

286

287

# More flexible move operation

288

moved_item = client.lmove('source', 'destination', 'LEFT', 'RIGHT')

289

print(f"Moved item: {moved_item.decode()}")

290

```

291

292

### Conditional Push Operations

293

294

```python

295

import fakeredis

296

297

client = fakeredis.FakeRedis()

298

299

# Push only if list exists

300

result = client.lpushx('nonexistent', 'value')

301

print(f"Push to nonexistent list: {result}") # 0 (failed)

302

303

# Create the list first

304

client.lpush('existing', 'initial')

305

306

# Now pushx will work

307

result = client.lpushx('existing', 'new_item')

308

print(f"Push to existing list: {result}") # 2 (new length)

309

310

result = client.rpushx('existing', 'another_item')

311

print(f"Right push to existing list: {result}") # 3 (new length)

312

```

313

314

### Producer-Consumer Pattern

315

316

```python

317

import fakeredis

318

import threading

319

import time

320

import random

321

322

def producer(client, queue_name, producer_id):

323

"""Produce items and add them to the queue"""

324

for i in range(5):

325

item = f"item_{producer_id}_{i}"

326

client.rpush(queue_name, item)

327

print(f"Producer {producer_id} added: {item}")

328

time.sleep(random.uniform(0.1, 0.5))

329

330

def consumer(client, queue_name, consumer_id):

331

"""Consume items from the queue"""

332

while True:

333

# Block for up to 5 seconds waiting for an item

334

result = client.blpop(queue_name, timeout=5)

335

if result:

336

_, item = result

337

print(f"Consumer {consumer_id} processed: {item.decode()}")

338

time.sleep(random.uniform(0.2, 0.8)) # Simulate processing

339

else:

340

print(f"Consumer {consumer_id} timed out, stopping")

341

break

342

343

# Usage

344

client = fakeredis.FakeRedis()

345

queue_name = 'work_queue'

346

347

# Start producers and consumers

348

threads = []

349

350

# Start 2 producers

351

for i in range(2):

352

t = threading.Thread(target=producer, args=(client, queue_name, i))

353

threads.append(t)

354

t.start()

355

356

# Start 3 consumers

357

for i in range(3):

358

t = threading.Thread(target=consumer, args=(client, queue_name, i))

359

threads.append(t)

360

t.start()

361

362

# Wait for all threads to complete

363

for t in threads:

364

t.join()

365

366

print("All producers and consumers finished")

367

```

368

369

### Activity Feed Implementation

370

371

```python

372

import fakeredis

373

import time

374

import json

375

376

class ActivityFeed:

377

def __init__(self, client, user_id, max_items=100):

378

self.client = client

379

self.key = f'feed:{user_id}'

380

self.max_items = max_items

381

382

def add_activity(self, activity_type, data):

383

"""Add a new activity to the feed"""

384

activity = {

385

'type': activity_type,

386

'data': data,

387

'timestamp': int(time.time())

388

}

389

390

# Add to the left (most recent first)

391

self.client.lpush(self.key, json.dumps(activity))

392

393

# Keep only the most recent max_items

394

self.client.ltrim(self.key, 0, self.max_items - 1)

395

396

def get_activities(self, limit=10):

397

"""Get the most recent activities"""

398

activities = self.client.lrange(self.key, 0, limit - 1)

399

return [json.loads(activity.decode()) for activity in activities]

400

401

def get_activity_count(self):

402

"""Get total number of activities in feed"""

403

return self.client.llen(self.key)

404

405

# Usage

406

client = fakeredis.FakeRedis()

407

feed = ActivityFeed(client, 'user123')

408

409

# Add various activities

410

feed.add_activity('login', {'ip': '192.168.1.1'})

411

feed.add_activity('purchase', {'item': 'laptop', 'amount': 999.99})

412

feed.add_activity('comment', {'post_id': 456, 'text': 'Great post!'})

413

feed.add_activity('like', {'post_id': 789})

414

415

# Retrieve recent activities

416

recent = feed.get_activities(limit=5)

417

for activity in recent:

418

print(f"{activity['type']}: {activity['data']} at {activity['timestamp']}")

419

```

420

421

### Batch Processing with Pop Multiple

422

423

```python

424

import fakeredis

425

426

client = fakeredis.FakeRedis()

427

428

# Add batch of items to process

429

items = [f'task_{i}' for i in range(10)]

430

client.rpush('batch_queue', *items)

431

432

# Process items in batches

433

batch_size = 3

434

while client.llen('batch_queue') > 0:

435

# Pop multiple items at once (Redis 6.2+)

436

batch = client.lpop('batch_queue', count=batch_size)

437

438

if isinstance(batch, list):

439

# Multiple items returned

440

print(f"Processing batch: {[item.decode() for item in batch if item]}")

441

else:

442

# Single item or None

443

if batch:

444

print(f"Processing single item: {batch.decode()}")

445

break

446

```