Created
March 1, 2018 00:50
-
-
Save bbuchalter/1adf16d11616a57d6be75961cae2a526 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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