I’ve been using Redis extensively in a project in the last couple of weeks. And this post is on a small trick I had to resort to while using sorted sets. If you don’t already know, the rank of an element in a Redis sorted set is its index in the sorted order. And rank starts at 0.

Let’s create a sorted set of three members A, B, C with scores 40, 10 and 40.

redis.zadd 'myset', 40, 'A'
redis.zadd 'myset', 10, 'B'
redis.zadd 'myset', 40, 'C'

And when you do redis.zrange 'myset', 0, -1, you get ["B", "A", "C"] and you can see that B is ranked first (rank 0), followed by A (rank 1) and then C (rank 2). This can also be verified by calling zrank on each item.

redis.zrank 'myset', 'A'
=> 1
redis.zrank 'myset', 'B'
=> 0
redis.zrank 'myset', 'C'
=> 2

My use case required the same rank for same scores and this is how I made Redis do what I wanted:

  • I created a sorted set with the scores being the members themselves.

redis.zadd 'newset', 10, 10
redis.zadd 'newset', 40, 40

  • Then I saved the member-score mapping in a hash separately.

redis.hset 'myhash', 'A', 40
redis.hset 'myhash', 'B', 10
redis.hset 'myhash', 'C', 40

  • Now, when I need the rank of a member, I just need to look up the member’s score in the hash and find the rank of that score from the new sorted set.

r = redis.hget 'myhash', 'A'
=> "40"
redis.zrank 'newset', r
=> 1
r = redis.hget 'myhash', 'B'
=> "10"
redis.zrank 'newset', r
=> 0
r = redis.hget 'myhash', 'C'
=> "40"
redis.zrank 'newset', r
=> 1

That is, rank(A) and rank(C) are equal to rank(40) and rank(B) is equal to rank(10). This gives rank 1 for A and C and rank 0 for B which is exactly what I wanted. Yay!