0

I am trying to check if an array starts with another array.

I managed to write some code that suits my use case:

def self.array_start_with?(ary, beginning)
  return true if beginning.empty?

  ary[0..(beginning.size - 1)] == beginning
end

but it's not very beautiful code, and I wonder if there is a more idiomatic way. The first code line within the method body is necessary when the beginning is empty but ary isn't. Otherwise the beginning will be compared to ary as a whole (ary[0..-1]). I could write:

ary[0..[beginning.size - 1, 0].max]

but that's uglier.

The second line of code doesn't honor the order of elements as [1, 2] == [2, 1] is true in Ruby. In my specific implementation, this isn't an issue as there can only be one order of the elements in the input but I'm still curious whether a more general solution exists.

Edit: Brain fart on [1, 2] == [2, 1] being true. Sorry.

3
  • What version of ruby are you using? [1, 2] == [2, 1] should be false. Commented Jan 4, 2018 at 17:44
  • Your code works fine. You can also say ary.first(beginning.length) == beginning, reads a little clearer. Or you could say arr[0...beginning.length] == beginning (triple periods means "up to but not including") Commented Jan 4, 2018 at 17:45
  • Jacob: You are absolutely right. I must have had a complete brain fart. Commented Jan 4, 2018 at 19:45

2 Answers 2

3

Here's one way to do it:

ary.take(beginning.size) == beginning

One advantage is that this works for empty arrays, so you don't need the extra check.

Sign up to request clarification or add additional context in comments.

Comments

1

I needed to perform this calculation very fast, so I created a benchmark with 7 methods as candidates:

require 'benchmark'

def candidate1(a, b)
  return false if b.size > a.size

  b.each_with_index do |el, i|
    return false if a[i] != el
  end

  true
end

def candidate2(a, b)
  return false if b.size > a.size

  i = 0
  b.each do |el|
    return false if a[i] != el
    i += 1
  end

  true
end

def candidate3(a, b)
  return false if b.size > a.size

  0.upto(b.size) do |i|
    return false if a[i] != b[i]
  end

  true
end

def candidate4(a, b)
  0.upto(b.size) do |i|
    return false if a[i] != b[i]
  end

  true
end

def candidate5(a, b)
  i = 0
  b.each do |el|
    return false if a[i] != el
    i += 1
  end

  true
end

def candidate6(a, b)
  return false if b.size > a.size

  i = -1
  b.each do |el|
    return false if a[i+=1] != el
  end

  true
end

def candidate7(a, b)
  a.take(b.size) == b
end

TEST_CASES = [
  [[3,3,3,3], [3,3,3,3]],
  [[3,3], [3,3,3,3]],
  [[3,3,3,3], [3,3]],
  [[1,2,3,4], [5,6,7,8]],
  [[1,2,3,4], [5,6,7]],
  [[], []],
  [[1,2,3,4],[1,2,3]],
  [[1,2,3,4,5,6,7,8,9,10,11,12,13],[1,2,3]],
  [[1,2,3],[1,2,3,4,5,6,7,8,9,10,11,12,13]]
]

N = 1_000_000

Benchmark.bm do |x|
  puts "Running all testcases #{N} times on each candidate"

  i = 1.upto(7) do |i|
    x.report do
      method_name = "candidate#{i}"
      puts method_name

      N.times do
        TEST_CASES.each_with_index do |c, j|
          send(method_name, c[0], c[1])
        end
      end
    end
  end
end

Here's what I got:

candidate1
 4.713704   0.010974   4.724678 (  4.742682)
candidate2
 3.867821   0.009236   3.877057 (  3.888543)
candidate3
 5.521557   0.014552   5.536109 (  5.577994)
candidate4
 6.403158   0.010979   6.414137 (  6.428128)
candidate5
 4.730323   0.010388   4.740711 (  4.753335)
candidate6
 3.891551   0.021362   3.912913 (  3.964875)
candidate7
 3.942376   0.008003   3.950379 (  3.964634)

To summarise: candidates 2 and 6 were always outperforming others, sometimes one was winning, sometimes another:

def candidate2(a, b)
  return false if b.size > a.size

  i = 0
  b.each do |el|
    return false if a[i] != el
    i += 1
  end

  true
end

def candidate6(a, b)
  return false if b.size > a.size

  i = -1
  b.each do |el|
    return false if a[i+=1] != el
  end

  true
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.