/ Tags: RUBY-CODE / Categories: SOLUTIONS

Count Unique Values Across Nested Arrays In Ruby Without Duplicates

Tags across a set of posts, permissions across a set of roles, skills across a team — the data arrives as an array of arrays and you need the distinct total, or the frequency of each value across all the inner collections.

Description

flatten collapses the nesting, then uniq.size gives the distinct count. flat_map does the flatten and the extraction in one pass when the inner arrays need to be reached through a key or a method. For frequency rather than a plain count, tally returns a hash of value to occurrence count in a single pass — faster and clearer than group_by plus transform_values. Counting distinct containers per value — how many posts use each tag — needs uniq on the inner array first, or a post tagged “ruby” twice counts twice.

Sample input:

  posts = [
    { title: "A", tags: ["ruby", "rails", "ruby"] },
    { title: "B", tags: ["rails", "hotwire"] },
    { title: "C", tags: ["ruby"] }
  ]


Sample Output:

  # distinct tags => 3
  # frequency     => { "ruby" => 2, "rails" => 2, "hotwire" => 1 }

Answer

  nested = [["ruby", "rails"], ["rails", "hotwire"], ["ruby"]]

  nested.flatten.uniq.size
  # => 3

  nested.flatten.uniq
  # => ["ruby", "rails", "hotwire"]

  # Reach into a key — one pass
  posts.flat_map { |p| p[:tags] }.uniq.size
  # => 3

  # Frequency across everything, duplicates within a post included
  posts.flat_map { |p| p[:tags] }.tally
  # => { "ruby" => 3, "rails" => 2, "hotwire" => 1 }

  # How many POSTS use each tag — dedupe within each post first
  posts.flat_map { |p| p[:tags].uniq }.tally
  # => { "ruby" => 2, "rails" => 2, "hotwire" => 1 }

  # Only values appearing in more than one post
  posts.flat_map { |p| p[:tags].uniq }
       .tally
       .select { |_, count| count > 1 }
       .keys
  # => ["ruby", "rails"]

  # Arbitrary nesting depth
  [[1, [2, 3]], [3, [4]]].flatten.uniq.size   # => 4

  # Large collections — Set avoids building the intermediate array
  require "set"
  posts.each_with_object(Set.new) { |p, set| set.merge(p[:tags]) }.size

Learn More

cdrrazan

Rajan Bhattarai

Full Stack Software Developer! 💻 🏡 Grad. Student, MCS. 🎓 Class of '23. GitKraken Ambassador 🇳🇵 2021/22. Works with Ruby / Rails. Photography when no coding. Also tweets a lot at TW / @cdrrazan!

Read More