1

Preferably in Ruby

I need a way to determine if one array is a "subarray" of another array when order matters

For instance,

a = ["1", "4", "5", "7", "10"]
b = ["1", "4", "5"]
c = ["1", "5", "4"]
d = ["1", "5", "10"]

a includes b = true
a includes c = false
a include d = false

Thanks in advance.

newsn31
  • 217
  • 3
  • 9

4 Answers4

4
[b, c, d].map do |arr|
  a.each_cons(arr.length).any?(&arr.method(:==))
end
#⇒ [true, false, false]

This is definitely not the most performant solution, but for not huge arrays is works and, which is important, is readable.

Enumerable#each_cons.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
0
a = ["1", "4", "5", "7", "10"]
b = ["1", "4", "5"]
c = ["1", "5", "4"]
d = ["1", "5", "10"]


def sub_set?(arr_a, arr_b)
  arr_a.select.with_index do |a, index|
    arr_b[index] == a
  end == arr_b
end

puts "testing sub_set #{a.inspect} and #{c.inspect}"
puts sub_set?(a,c).inspect
lacostenycoder
  • 10,623
  • 4
  • 31
  • 48
0
class Array
  def includes(array)
    return false if array.size > self.size
    indexes = []
    self.each_with_index {|e, i| indexes << i if e == array[0]}
    p indexes
    indexes.each do |i|
      return true if self[i..i+array.size-1] == array
    end
    return false
  end
end

p a.includes b
p a.includes c
p a.includes d

Or less phpish :)

class Array
  def includes(array)
    return false if array.size > self.size
    indexes = each.with_index.select { |e, i| e == array[0] }.map(&:last)
    indexes.each {|i| self[i..i+array.size-1] == array ? (return true) : (return false)}
  end
end

p a.includes b
p a.includes c
p a.includes d
iGian
  • 11,023
  • 3
  • 21
  • 36
0

You could join the elements with an arbitrary character then compare strings:

a.join(' ').match?(array.join(' '))

This works for the test cases you have provided:

a.join(' ').match?(b.join(' '))                                                                                                                     
 #=> true
a.join(' ').match?(c.join(' '))                                                                                                                     
 #=> false
a.join(' ').match?(d.join(' '))                                                                                                                   
 #=> false

But this is not a general solution and will fail on varying types of arrays (see comments for further discussion).

Sagar Pandya
  • 9,323
  • 2
  • 24
  • 35
  • This fails if array contains non-strings. The method should work for any array values. – lacostenycoder Apr 28 '18 at 09:37
  • Good one. @lacostenycoder `joiner = 1..Float::INFINITY.each { |i| break chr(i) unless a.join.match? chr(i) }`. – Aleksei Matiushkin Apr 28 '18 at 09:41
  • @lacostenycoder Your first sentence is incorrect. If the OP had integer elements instead of string arrays, my approach still works:) – Sagar Pandya Apr 28 '18 at 09:48
  • @SagarPandya I would have done it this way in 30 seconds if I though that was a good answer. What if the array had Hashes or some other non string values? But we have plenty of examples now in any case. – lacostenycoder Apr 28 '18 at 09:49
  • 2
    @lacostenycoder Okay, this is probably not a robust or work-for-all approach, but it can be used in many cases (an array of strings or an array of integers for example). Which may suffice for the OP's needs, so it's worth mentioning as an answer. – Sagar Pandya Apr 28 '18 at 10:22
  • 1
    @SagarPandya. I was gonna do it this way but thought I'd get slammed or down voted so I tried to find something which didn't rely on string conversion. This way is the easy way. I'm suffering from insomnia so sorry for the down vote. But also see https://stackoverflow.com/questions/33174985/how-do-you-check-if-one-array-is-a-subsequence-of-another/50074743#50074743 because this was tagged as duplicate when I started. Make some edit and I'll reverse it. – lacostenycoder Apr 28 '18 at 10:35
  • 1
    @lacostenycoder Yes I was just looking for a cheeky shortcut, but I should have mentioned its shortcomings in my answer. Your downvote is fine, you have a valid reason. I only dislike downvotes when no or an invalid reason is given. – Sagar Pandya Apr 28 '18 at 10:54
  • This fails for example for `a = ['a b']; b = %w[a b]`. – Jörg W Mittag Apr 28 '18 at 15:55
  • 1
    Had there been no downvotes I probably wouldn't have upvoted, but I don't think downvotes are deserved, so I've upvoted to offset up to five of them. I don't believe the lack of robustness is a fair objection as the OP's example suggests that all the array elements are strings (of digits). – Cary Swoveland Apr 28 '18 at 18:49
  • @JörgWMittag That's a neat counter example which I should have been aware of. I may delete this answer or at the very least add a caveat alerting the reader to this answers limitations. – Sagar Pandya Apr 28 '18 at 18:52
  • @Cary Ta. Will update my answer to reflect some of the points (including yours) made in the comments. Had I added a commentary in the first place I may have saved others from pointing out the limitations. – Sagar Pandya Apr 28 '18 at 19:05