Three solutions: brutish (#3, initial answer), better (#2, first edit) and best (#1, variant of @Stefan's answer, second edit).
a = [:foo, 1, :foo, 0, nil, :bar, "baz", false]
b = [:foo, 1, :foo, 0, true, "baz", false]
c = [:foo,1,:goo]
d = [:goo,1,:new]
Note b
is slightly changed from the OP's example.
Unless otherwise noted below, the common suffix would be calculated by reversing the arrays, applying common_prefix
and then reversing the result.
#1
Variant of Stefan's answer that rids it of zip
and map
(and retains his technique of truncating one array to at most the length of the other):
def common_prefix(a,b)
a[0,b.size].take_while.with_index { |e,i| e == b[i] }
end
common_prefix(a,b)
#=> [:foo, 1, :foo, 0]
common_prefix(c,d)
#=> []
#2
def common_prefix(a,b)
any, arr = a.zip(b).chunk { |e,f| e==f }.first
any ? arr.map(&:first) : []
end
def common_suffix(a,b)
any, arr = a[a.size-b.size..-1].zip(b).chunk { |e,f| e==f }.to_a.last
any ? arr.map(&:first) : []
end
common_prefix(a,b)
#=> [:foo, 1, :foo, 0]
# Nore: any, arr = a.zip(b).chunk { |e,f| e==f }.to_a
# => [[true, [[:foo, :foo], [1, 1], [:foo, :foo], [0, 0]]],
# [false, [[nil, true], [:bar, :baz], ["baz", false], [false, nil]]]]
common_suffix(a,b)
#=> ["baz", false]
# Note: any, arr = a[a.size-b.size..-1].zip(b).chunk { |e,f| e==f }.to_a
# => [[false, [[1, :foo], [:foo, 1], [0, :foo], [nil, 0], [:bar, true]]],
# [true, [["baz", "baz"], [false, false]]]]
When :first
is sent to the enumerator Enumerable#chunk the first element of the enumerator is returned. It therefore should be comparable in efficiency to using Enumerable#take_while.
common_prefix(c,d)
#=> []
common_suffix(c,d)
#=> []
#3
def common_prefix(a,b)
a[0,(0..[a.size, b.size].min).max_by { |n| (a[0,n]==b[0,n]) ? n : -1 }]
end
common_prefix(a,b)
#=> [:foo, 1, :foo, 0]
common_prefix(c,d)
#=> []