Skip to main content
Show "thanks" with upvotes
Source Link
Jamal
  • 34.9k
  • 13
  • 133
  • 237

I've just completed this binary search kata - which has some weird requirements - and I spent a little while "cleaning" up my code and making it more compact. I would appreciate some feedback on the readability of the BinarySearchBinarySearch class. I've packed a lot of logic into some of the lines and I'd like to know if that makes it more or less readable. Thanks!

And, the BinarySearchBinarySearch Class:

I've just completed this binary search kata - which has some weird requirements - and I spent a little while "cleaning" up my code and making it more compact. I would appreciate some feedback on the readability of the BinarySearch class. I've packed a lot of logic into some of the lines and I'd like to know if that makes it more or less readable. Thanks!

And, the BinarySearch Class:

I've just completed this binary search kata - which has some weird requirements - and I spent a little while "cleaning" up my code and making it more compact. I would appreciate some feedback on the readability of the BinarySearch class. I've packed a lot of logic into some of the lines and I'd like to know if that makes it more or less readable.

And, the BinarySearch Class:

Source Link
user341493
  • 351
  • 1
  • 9

BinarySearch Kata in Ruby

I've just completed this binary search kata - which has some weird requirements - and I spent a little while "cleaning" up my code and making it more compact. I would appreciate some feedback on the readability of the BinarySearch class. I've packed a lot of logic into some of the lines and I'd like to know if that makes it more or less readable. Thanks!

A Ruby Kata for Binary Search of Arrays:

Binary Search Kata
   Create a binary search class that is initialized with an array of integers
      - detail: Do not use built in array functions
      - detail: The initial array is [1,3]
      - detail: return the index of the search value if found
      - example: searching for 3 returns 1

completed (Y|n):

   Successfully deal with values that are not in the data set
      - example: searching for 5 indicates to the caller the value is not found

completed (Y|n):

   Binary Search handles an odd number of elements in the data set
      - detail: Allows the data set to be redefined with [1,3,5,7,9]
      - example: Searching for 5 returns 2
      - example: Searching for 7 returns 3

completed (Y|n):

   Handles more than trivial sized data set
      - detail: consider a data set of the first 10000 integers
      - example: Searching for 899 returns 898

completed (Y|n):

   Supports duplicate elements in the data set
      - detail: Use [1,3,5,5,7,9] as the data set
      - example: Searching for 3 returns 1
      - example: Searching for 5 returns 2

completed (Y|n):

   Handles expired call for duplicate elements
      - detail: Use [1,3,5,5,7,9] as the data set
      - example: Third call to search for 5 is handled like missing element

completed (Y|n):

   Supports sorted array of strings
      - detail: Use ["a", "b", "c", "d"] as the data set
      - example: Searching for "c" returns 2

completed (Y|n):

   Supports duplicate strings
      - detail: Use ["a", "b", "c", "c", "d"] as the data set
      - example: First search for "c" returns 2
      - example: Second search for "c" returns 3

completed (Y|n):

   Handles expired call for duplicate string elements
      - detail: Use ["a", "b", "c", "c", "d"] as the data set
      - example: Third call to search for "c" is handled like missing element

completed (Y|n):

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Requirement                                                                      ┃ Time     ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╊━━━━━━━━━━┫
┃ Create a binary search class that is initialized with an array of integers       ┃ 00:04:02 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╊━━━━━━━━━━┫
┃ Successfully deal with values that are not in the data set                       ┃ 00:04:09 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╊━━━━━━━━━━┫
┃ Binary Search handles an odd number of elements in the data set                  ┃ 00:10:49 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╊━━━━━━━━━━┫
┃ Handles more than trivial sized data set                                         ┃ 00:02:28 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╊━━━━━━━━━━┫
┃ Supports duplicate elements in the data set                                      ┃ 00:05:17 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╊━━━━━━━━━━┫
┃ Handles expired call for duplicate elements                                      ┃ 00:10:35 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╊━━━━━━━━━━┫
┃ Supports sorted array of strings                                                 ┃ 00:02:27 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╊━━━━━━━━━━┫
┃ Supports duplicate strings                                                       ┃ 00:02:35 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╊━━━━━━━━━━┫
┃ Handles expired call for duplicate string elements                               ┃ 00:00:57 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━┛

Questions:
   - What about supporting duplicate data changed your solution?
   - Did your approach get better or worse after dealing with duplicates?
   - What was the hardest part about adding support for strings?
   - Can you think of a different technique for building the kata?
   - What can you say about the relative merits of the various techniques you chose?
   - Which is the most likely to make it into production code?
   - Which was the most fun to write?
   - Which requirement was the hardest to get working?
   - For all of the above, ask yourself why?

My Specs:

  1 require 'spec_helper'
  2 require 'binary_search'
  3 require 'its'
  4
  5 describe BinarySearch do
  6   subject(:binary_search) { BinarySearch.new(data) }
  7   let(:data) { [1,3] }
  8
  9   describe "#initialize" do
 10     it "instantiates" do
 11       expect { binary_search }.to_not raise_exception
 12     end
 13   end
 14
 15   describe '.find' do
 16     its(:find, 3) { should eq(1) }
 17     its(:find, 5) { should be_nil }
 18
 19     context 'odd numbers of elements in data set' do
 20       let(:data) { [1,3,5,7,9] }
 21       its(:find, 0) { should be_nil }
 22       its(:find, 1) { should eq(0) }
 23       its(:find, 2) { should be_nil }
 24       its(:find, 3) { should eq(1) }
 25       its(:find, 4) { should be_nil }
 26       its(:find, 5) { should eq(2) }
 27       its(:find, 6) { should be_nil }
 28       its(:find, 7) { should eq(3) }
 29       its(:find, 8) { should be_nil }
 30       its(:find, 9) { should eq(4) }
 31       its(:find, 10) { should be_nil }
 32     end
 33
 34     context 'non trivial data sets' do
 35       let(:data) { [] }
 36       before { for i in 1..10000 do data << i end }
 37       its(:find, 899) { should eq(898) }
 38     end
 39
 40     context 'duplicate data elements' do
 41       let(:data) { [1,3,5,5,7,9] }
 42       its(:find, 3) { should eq(1) }
 43       its(:find, 5) { should eq(2) }
 44       it 'handles expired calls for duplicate elements' do
 45         2.times do binary_search.find(5) end
 46         binary_search.find(5).should be_nil
 47       end
 48     end
 49
 50     context 'sorted array of strings' do
 51       let(:data) { ['a','b','c','d'] }
 52       its(:find, 'a') { should eq(0) }
 53       its(:find, 'b') { should eq(1) }
 54       its(:find, 'c') { should eq(2) }
 55       its(:find, 'd') { should eq(3) }
 56       its(:find, 'x') { should be_nil }
 57     end
 58
 59     context 'duplicate strings' do
 60       let(:data) { ['a', 'b', 'c', 'c', 'd'] }
 61       its(:find, 'c') { should eq(2) }
 62       it 'handles multiple searches' do
 63         binary_search.find('c')
 64         binary_search.find('c').should eq(3)
 65       end
 66       it 'handles expired calls for strings' do
 67         2.times do binary_search.find('c') end
 68         binary_search.find('c').should be_nil
 69       end
 70     end
 71   end
 72 end

And, the BinarySearch Class:

  1 class BinarySearch
  2   def initialize(data)
  3     @data = data
  4     @search_hash = Hash.new
  5   end
  6
  7   def find(target)
  8     return @search_hash[target] = first_find(target) unless @search_hash[target]
  9     return @data[@search_hash[target] + 1] == target ? @search_hash[target] += 1 : nil
 10   end
 11
 12   private
 13
 14   def first_find(target)
 15     return nil unless pivot = bsearch(target, @data)
 16     pivot -= 1 while @data[pivot - 1] == target
 17     return pivot
 18   end
 19
 20   def bsearch(target, data)
 21     pivot = data.length / 2
 22
 23     return pivot if data[pivot] == target
 24     return nil if pivot == 0
 25
 26     if data[pivot] > target
 27       return bsearch(target, data[0..pivot - 1])
 28     else
 29       offset = bsearch(target, data[pivot..-1])
 30       return offset ? pivot + offset : nil
 31     end
 32   end
 33 end