Skip to content

Instantly share code, notes, and snippets.

@bbuchalter
Created March 1, 2018 00:50
Show Gist options
  • Save bbuchalter/1adf16d11616a57d6be75961cae2a526 to your computer and use it in GitHub Desktop.
Save bbuchalter/1adf16d11616a57d6be75961cae2a526 to your computer and use it in GitHub Desktop.
# Given a file containing order line items in the form [User ID]: [Medications] where medications are separated by commas, determine the pairs of medications that appear most frequently together.
# For example:
# Given two line items
# Zestril, atorvastatin, Levoxine
# Zestril, atorvastatin, Orasone
# The function should output the pair Zestril, atorvastatin.
medications = [
"5fd8175f-a31c-48a2-9eb3-75b1b5fea7d5: Thyrox, Zoloft",
"5fd8175f-a31c-48a2-9eb3-75b1b5fea7d5: Thyrox, Zoloft",
"d221dc55-8339-4656-a34f-38524f47198b: Ambien, Genebrom DM",
"fde98c46-788a-40df-86fc-00230d0ba932: estradiol, Synthroid, metformin",
"15f1c67f-be72-4aab-9017-a1b2f1ee02b0: Levoxine",
"f5ff2aed-8b4b-4f1d-9f76-07f1fa064f08: amoxicillin-pot clavulanate",
"247d4229-c84a-4d7a-ab34-5942035eeb87: Zoloft, Wellbutrin XL, Thyrox",
"bfdd6170-11d1-47b9-8afc-1f27088b1bb2: Amitid, Levotabs, Ambien",
"8c0c0d29-05eb-4a74-8dad-91db34557653: Synthroid",
"96ba3867-3500-4cbd-aef4-364f833d59ee: ranitidine Levoxine",
"df95d102-f209-4709-a3fc-0c3536c31ad9: Zocor, Thyrox, Synthroid",
"8f5f64bb-c017-437f-912b-27e7f06e812a: lamotrigine",
"b5b86823-c9ab-4e0c-8beb-a8e9c6c7e869: ranitidine, Zoloft",
"81274481-4ef9-4842-8cd2-6b238788b0c8: Synthroid, atenolol",
"13f2c852-a8ad-406b-8d3e-722ec985b3dd: Ambien",
"6ba197ae-2f45-4953-819e-83ddf0072d62: Zoloft, Levoxine",
"a7340d49-58dc-482d-a012-e38f8f365c8c: Thyrox, lamotrigine, Zoloft",
"7d688c7c-cb39-4667-82bd-4d732033e5dc: lamotrigine, Levoxine",
"c0b8eb59-3af2-4502-a321-538de2a1a5bf: Zocor",
"0e30def3-05c0-4699-8906-1317610984df: Toprol XL",
"83fabf17-d36a-4192-a415-2e9de2591ebf: risperidone, Zoloft, Levoxine"]
require 'rspec/autorun'
require 'set'
require 'digest'
class LineStringToLine
attr_reader :meds
def initialize(line)
@line = line
user_id, meds = line.split(": ")
@user_id = user_id
@meds = meds.split(", ")
end
private
attr_reader :line
end
class LineToPairs
def initialize(line)
@line = line
end
def pairs
line.meds.permutation.reduce(Set.new) do |set, permutation|
pair = Pair.new([
permutation[0],
permutation[1]
])
set.add(pair) if pair.valid?
end.to_a
end
private
attr_reader :line
end
class Pair
attr_accessor :first, :second
def initialize(pair)
@valid = pair.compact.uniq.length == 2
if @valid
sorted_pair = pair.sort # OPTIMIZE: MORE LOOPS!
@first = sorted_pair.first
@second = sorted_pair.last
end
end
def ==(other)
first == other.first && second == other.second
end
def eql?(other)
self.==(other)
end
def to_s
"#{first}/#{second}"
end
def inspect
"#<Pair: #{key}>"
end
def hash
key.each_char.inject(0) { |counter, char| counter += char.ord }
end
def valid?
@valid
end
private
def key
"#{first},#{second}"
end
end
RSpec.describe LineStringToLine do
describe "#meds" do
subject { described_class.new(line).meds }
context "given a line as a string" do
let(:line) { "5fd8175f-a31c-48a2-9eb3-75b1b5fea7d5: A, B, C" }
it "returns an array of meds from that line" do
expect(subject).to eq(%w(A B C))
end
end
end
end
RSpec.describe LineToPairs do
subject { described_class.new(line).pairs }
context "given an order line item" do
let(:line) { double('Line', meds: %w(A B C)) }
it "returns a Pair for each perumation" do
expect(subject).to match_array([
Pair.new(%w(A B)),
Pair.new(%w(A C)),
Pair.new(%w(B C))
])
end
end
context "given an order line item with no pair" do
let(:line) { double('Line', meds: %w(A)) }
it { is_expected.to eq([]) }
end
end
RSpec.describe Pair do
shared_examples "#==" do
context "when strings in the pair are different" do
let(:first_pair) { described_class.new(%w(A C)) }
let(:second_pair) { described_class.new(%w(A B)) }
it { is_expected.to be_falsey }
end
context "when strings in the pair are the same" do
let(:first_pair) { described_class.new(%w(A B)) }
let(:second_pair) { described_class.new(%w(A B)) }
it { is_expected.to be_truthy }
end
context "when strings in the pair are the same, but in different orders" do
let(:first_pair) { described_class.new(%w(A B)) }
let(:second_pair) { described_class.new(%w(B A)) }
it { is_expected.to be_truthy }
end
end
describe "#==" do
subject { first_pair == second_pair }
it_behaves_like "#=="
end
describe "#eql?" do
subject { first_pair.eql?(second_pair) }
it_behaves_like "#=="
end
describe "#hash" do
subject { described_class.new(%w(A B)).hash }
it { is_expected.to eq(175) }
context "identical pairs return identical hashes" do
let(:first_hash) { described_class.new(%w(A B)).hash }
let(:second_hash) { described_class.new(%w(B A)).hash }
subject { first_hash == second_hash }
it { is_expected.to be_truthy }
end
end
describe "#valid?" do
subject { described_class.new(elements).valid? }
context "given one element" do
let(:elements) { %w(A) }
it { is_expected.to be_falsey }
end
context "given three elements" do
let(:elements) { %w(A B C) }
it { is_expected.to be_falsey }
end
context "given two elements" do
let(:elements) { %w(A B) }
it { is_expected.to be_truthy }
end
context "given two of the same elements" do
let(:elements) { %w(A A) }
it { is_expected.to be_falsey }
end
end
end
# TIME TO EXECUTE
lines = medications.map { |line| LineStringToLine.new(line) }
pairs = lines.map { |line| LineToPairs.new(line).pairs }
aggregate = pairs.flatten.reduce(Hash.new(0)) do |aggregate, pair|
aggregate[pair] += 1
aggregate
end
sorted_aggregate = aggregate.sort_by do |pair, value|
value
end
most_popular_pair, frequency = sorted_aggregate.last
puts "The most popular pair of meds is #{most_popular_pair}, with a frequency of #{frequency}."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment