A Kata a day: Inject part 2
This is the continuation of a kata that I did not finish in the hour that I normally employ on practicing with katas. So here it goes:
First I will show you the specs that needs to be implemented:
context '#nested_key' do
it 'finds the nested key when present' do
data = { outer: { inner: 'value' } }
expect(nested_key([:outer, :inner], data)).to eq('value')
end
it 'returns nil when missing a level' do
data = { other: 'value' }
expect(nested_key([:outer, :inner], data)).to be_nil
end
end
Looks pretty straightforward let me write down the implementation
def nested_keys(keys, hash)
keys.inject(hash) do |value, key|
value = value[key] if value
end
end
There’s something interesting about this implementation in my honest opinion and is that it doesn’t show intent; I won’t go ahead and refactor this piece of code but I will think about this for a while. Is inject better most of the time or not?
The next problem actually was a rewrite to use inject instead of the following:
def self.search(query)
# TODO: rewrite to use inject instead of each and mutation
relation = self
query.split(' ').each do |keyword|
relation = relation.where('body LIKE ?', "%#{keyword}%")
end
relation
end
And here are the specs:
context Category, '#search' do
it 'finds entries by keyword search' do
Category.create! name: 'one', body: 'keyword1 other words'
Category.create! name: 'two', body: 'keyword2'
Category.create! name: 'three', body: 'keyword1 keyword2 other words'
Category.create! name: 'four', body: 'keyword1 keyword2 other words'
result = Category.search('keyword1 keyword2').map(&:name)
expect(result).to match_array(%w(three four))
end
end
My rewrite
def self.search(query)
query.split(' ').inject(self) do |relation, keyword|
relation = relation.where('body LIKE ?', "%#{keyword}%")
end
end
Last spec:
context Category, '#find_by_path' do
it 'finds a top-level category' do
Category.create!(name: 'Findme')
expect(Category.find_by_path('Findme').try(:name)).to eq('Findme')
end
it 'finds a nested category' do
root = Category.create!(name: 'Root')
child = Category.create!(name: 'Child', parent: root)
Category.create!(name: 'Grandchild', body: 'Orphan')
Category.create!(name: 'Grandchild', parent: child, body: 'Expected')
Category.create!(name: 'Grandchild', body: 'Orphan')
result = Category.find_by_path('Root/Child/Grandchild').try(:body)
expect(result).to eq('Expected')
end
it 'returns nil when missing a level' do
Category.create!(name: 'Root')
result = Category.find_by_path('Root/Child/Grandchild')
expect(result).to be_nil
end
end
Implementation:
def self.find_by_path(path)
path.split('/').inject(self) do |category, path|
category.children.find_by_name(path)
end
end
The last one was a little bit hard to be honest; I spend a lot of time thinking about how to solve it and even this was some sort of epiphany based on reading somebody else code; so that is that.
What I learned from this kata?
- I need to spend more time reading the documentation of the language in question.