James Stewart
James Stewart

Reputation: 1064

How do you check for matching keys in a ruby hash?

I'm learning coding, and one of the assignments is to return keys is return the names of people who like the same TV show.

I have managed to get it working and to pass TDD, but I'm wondering if I've taken the 'long way around' and that maybe there is a simpler solution?

Here is the setup and test:

class TestFriends < MiniTest::Test

  def setup

    @person1 = {
      name: "Rick",
      age: 12,
      monies: 1,
      friends: ["Jay","Keith","Dave", "Val"],
      favourites: {
        tv_show: "Friends",
        things_to_eat: ["charcuterie"]
      }
    }

    @person2 = {
      name: "Jay",
      age: 15,
      monies: 2,
      friends: ["Keith"],
      favourites: {
        tv_show: "Friends",
        things_to_eat: ["soup","bread"]
      }
    }

    @person3 = {
      name: "Val",
      age: 18,
      monies: 20,
      friends: ["Rick", "Jay"],
      favourites: {
        tv_show: "Pokemon",
        things_to_eat: ["ratatouille", "stew"]
      }
    }

    @people = [@person1, @person2, @person3]

      end

      def test_shared_tv_shows
        expected = ["Rick", "Jay"]

        actual = tv_show(@people)

        assert_equal(expected, actual)
      end
    end

And here is the solution that I found:

def tv_show(people_list)
  tv_friends = {}
  for person in people_list
    if tv_friends.key?(person[:favourites][:tv_show]) == false
      tv_friends[person[:favourites][:tv_show]] = [person[:name]]
    else
      tv_friends[person[:favourites][:tv_show]] << person[:name]
    end
  end
  for array in tv_friends.values()
     if array.length() > 1
      return array
    end
  end
end

It passes, but is there a better way of doing this?

Upvotes: 2

Views: 1672

Answers (1)

Sebasti&#225;n Palma
Sebasti&#225;n Palma

Reputation: 33420

I think you could replace those for loops with the Array#each. But in your case, as you're creating a hash with the values in people_list, then you could use the Enumerable#each_with_object assigning a new Hash as its object argument, this way you have your own person hash from the people_list and also a new "empty" hash to start filling as you need.

To check if your inner hash has a key with the value person[:favourites][:tv_show] you can check for its value just as a boolean one, the comparison with false can be skipped, the value will be evaluated as false or true by your if statement.

You can create the variables tv_show and name to reduce a little bit the code, and then over your tv_friends hash to select among its values the one that has a length greater than 1. As this will give you an array inside an array you can get from this the first element with first (or [0]).

def tv_show(people_list)
  tv_friends = people_list.each_with_object(Hash.new({})) do |person, hash|
    tv_show = person[:favourites][:tv_show]
    name    = person[:name]

    hash.key?(tv_show) ? hash[tv_show] << name : hash[tv_show] = [name]
  end
  tv_friends.values.select { |value| value.length > 1 }.first
end

Also you can omit parentheses when the method call doesn't have arguments.

Upvotes: 1

Related Questions