-1

I'd like to count the elements in y that exist in the same order than in x. So for:

x = [a,b,c,d,e,f,g,h]

y = [c,a,b,z,k,f,g,d,s,t]

I'd want a function that returns me a 4 as 'a','b','c','d' are in y but not "e" I'd like a function that returns 4. y is random but it never has any duplicates. x is constant and len(x) = 8. x and y are both lists of strings.

That means for:

x = [a,b,c,d,e,f,g,h]

y = [c,a,k,z,k,f,g,d,s,t]

I'd like the function to return 1.

I've tried something with a nested loop:

i = 0
h = 0
for s in x:
   for t in y:
      if s == t:
        i = i + 1 #i is what I'm looking for in the end.
        h = 0
      elif h = 9:
        break
      else:
        h = h + 1

My idea was to count the delta from one 't' to the next 't' but I can't get it to work properly as I just can't wrap my head around the required math. Thanks a lot for your suggestions already and please enjoy your day!

Difio
  • 145
  • 8

3 Answers3

2

In my previous answer, the code would throw an error when all elements of x were in y - so, here is my revised code:

print(([value in y for value in x] + [False]).index(False))

It does the job, but it's really hard to read. Let's split it up (the comments explain what each line does):

# This is our new list. In the previous code, this was a tuple - I'll get into
# that later. Basically, for each element in x, it checks whether that value is in
# y, resulting in a new list of boolean values. (In the last code, I used the map
# function with a lambda, but this is definitely more readable).

# For example, in OP's example, this list would look like
# [True, True, True, True, False, True, True, False]
new_list = [value in y for value in x]

# This is the step lacking with the old code and why I changed to a list.
# This adds a last False value, which prevents the index function from throwing an
# error if it doesn't find a value in the list (it instead returns the index this
# last False value is at). I had to convert from a tuple because
# you cannot add to a tuple, but you can add to a list. I was using a tuple in the
# last code because it is supposed to be faster than a list.
new_list_with_overflow = (new_list + [False])

# This is the final result. The index function gets the first element that is equal
# to False - meaning, it gets the index of the first element where x is not in y.
result = new_list_with_overflow.index(False)

# Finally, print out the result.
print(result)

Hopefully this explains what that one line is doing!

Some more links for reading:


Here is another (arguably less readable) code snippet:

print((*(value in y for value in x), False).index(False))

A benefit of this code is that it uses tuples, so it is faster than the previous code, with the drawback of being a bit harder to understand. It also is not supported by older versions of python. However, I can leave this as an exercise for you to figure out! You might want to check out what the * does.


EDIT: This is the new answer. The code below only works when all elements of x are not in y - otherwise, it throws an error. Also, these solutions are just more readable.

A "pythonic" one-liner:
print(tuple(map(lambda value: value in y, x)).index(False))
Ayush Garg
  • 2,234
  • 2
  • 12
  • 28
  • Wow this one is very impressive! I've never worked with `map` nor `lambda` nor do I quite understand how .index(false) makes sense but I will try to figure it out! Thanks again! – Difio Jun 26 '21 at 08:15
  • Ah, I do have another question. I was wondering why you chose a tuple instead of a list? Is it because I said that y is random and tuple handles different data types? I've tried your function with `list(...)` instead and it worked, too. – Difio Jun 26 '21 at 08:29
  • Okay. I've done a bit further research. Your function posts an error if all elements of x are represented in y. I've solved this with the following code: `while True: try: indexes = tuple(map(lambda value: value in y, x)).index(False) break except: indexes = len(x) break` – Difio Jun 26 '21 at 09:13
  • @Difio Oh, I forgot to test that! Here, I can edit my question with a better solution and an explanation as well! – Ayush Garg Jun 26 '21 at 15:52
0

Here's your function needed:

def counter(x, y):
    print("_" * 50)
    print("x: " + str(x))
    print("y: " + str(y))
    for i, letter in enumerate(x):
        if letter not in y:
            break
    print("i: " + str(i))
    return i

counter(
    ["a","b","c","d","e","f","g","h"], 
    ["c","a","b","z","k","f","g","d","s","t"]
)
counter(
    ["a","b","c","d","e","f","g","h"], 
    ["a","b","z","k","f","g","d","s","t"]
)
counter(
    ["a","b","c","d","e","f","g","h"], 
    ["c","a","b","z","k","f","g","d","s","t", "e"]
)

return:

__________________________________________________
x: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
y: ['c', 'a', 'b', 'z', 'k', 'f', 'g', 'd', 's', 't']
i: 4
__________________________________________________
x: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
y: ['a', 'b', 'z', 'k', 'f', 'g', 'd', 's', 't']
i: 2
__________________________________________________
x: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
y: ['c', 'a', 'b', 'z', 'k', 'f', 'g', 'd', 's', 't', 'e']
i: 7
Vincent Bénet
  • 1,212
  • 6
  • 20
0

Using itertools.takewhile:

from itertools import takewhile
result = len(list(takewhile(lambda item : item in y, x)))

It takes every item in x starting from the first item in x until the condition lambda item : item in y is no longer satisfied.

Kraigolas
  • 5,121
  • 3
  • 12
  • 37