Ruby
Redis

RedisのSorted Setsで同一スコアの場合、順位をランダムにしたい

タイトルの通り、Sorted Setsで取ってきたランキングで、同一のスコアの場合は順位をランダムにしたい。

# こんなデータがあったとして
ids_and_scores = [[10010818, 155.0],[10026928, 150.0],[10025650, 150.0],[10023988, 150.0],[10023708, 150.0],[10023820, 115.0],[10025925, 110.0],[10024209, 110.0]]

# ここと
[10026928, 150.0],[10025650, 150.0],[10023988, 150.0],[10023708, 150.0]

# ここの順番をランダムにしたい
[10025925, 110.0],[10024209, 110.0]

まずは重複しているスコアを取ってくる。

duplicate_scores = ids_and_scores.group_by { |id_score| id_score[1] }.select { |score, ary| 1 < ary.size }.keys
# => [150.0, 110.0]

重複しているスコアの場合は配列にまとめる。

last_score = 0
id_ary_tmp = ids_and_scores.inject([]) do |output, id_score|
  if duplicate_scores.include?(id_score[1])
    if output.last.class == Array && last_score == id_score[1]
      output.last << id_score[0]
    else
      output << [id_score[0]]
    end
  else
    output << id_score[0]
  end
  last_score = id_score[1]

  output
end
# => [10010818, [10026928, 10025650, 10023988, 10023708], 10023820, [10025925, 10024209]]

重複しているスコアの配列をシャッフルしてflattenで配列を作り直す。

id_ary = id_ary_tmp.map do |id_ids|
  if id_ids.class == Array
    id_ids.shuffle
  else
    id_ids
  end
end.flatten
# => [10010818, 10023988, 10025650, 10026928, 10023708, 10023820, 10024209, 10025925]

メソッドにまとめる。

# 同スコアの場合はランダムで順位づけしたい
def sort_id_considering_duplicate_scores(ids_and_scores)
  duplicate_scores = ids_and_scores.group_by { |id_score| id_score[1] }.select { |score, ary| 1 < ary.size }.keys
  last_score = 0
  id_ary_tmp = ids_and_scores.inject([]) do |output, id_score|
    if duplicate_scores.include?(id_score[1])
      if output.last.class == Array && last_score == id_score[1]
        output.last << id_score[0]
      else
        output << [id_score[0]]
      end
    else
      output << id_score[0]
    end
    last_score = id_score[1]
    output
  end

  id_ary = id_ary_tmp.map do |id_ids|
    if id_ids.class == Array
      id_ids.shuffle
    else
      id_ids
    end
  end
  id_ary.flatten
end

一応動いているっぽいけどもっとシンプルにできそう。。。

sort_id_considering_duplicate_scores(ids_and_scores)
# => [10010818, 10026928, 10023988, 10023708, 10025650, 10023820, 10024209, 10025925]

sort_id_considering_duplicate_scores(ids_and_scores)
# => [10010818, 10026928, 10023988, 10025650, 10023708, 10023820, 10024209, 10025925]

sort_id_considering_duplicate_scores(ids_and_scores)
# => [10010818, 10023708, 10025650, 10023988, 10026928, 10023820, 10024209, 10025925]

sort_id_considering_duplicate_scores(ids_and_scores)
# => [10010818, 10023988, 10025650, 10023708, 10026928, 10023820, 10024209, 10025925]

sort_id_considering_duplicate_scores(ids_and_scores)
# => [10010818, 10025650, 10023708, 10023988, 10026928, 10023820, 10025925, 10024209]

※バグがあったので一部修正。