chunpoon
chunpoon

Reputation: 970

Filter list of tuples for tuples that contain certain items python

I have a list of tuples like so:

a = [('1', '2', '5', '5', 'w', 'w', 'w', 'w'),
     ('1', '3', '5', '5', 'w', 'w', 'w', 'w'),
     ('1', '3', '4', '5', 'w', 'w', 'w', 'w'),
     ('1', '4', '4', '4', 'w', 'w', 'w', 'w'),
     ('1', '5', '5', '5', 'w', 'w', 'w', 'w')]

I want to be able to filter out the tuples that contain certain items. For example, I want to find all the tuples that contain '5', '5', 'w', 'w', 'w', 'w' specifically and place them in a list.

filter_for = ['5', '5', 'w', 'w', 'w', 'w']

Expected result would be:

result =  [('1', '2', '5', '5', 'w', 'w', 'w', 'w'),
           ('1', '3', '5', '5', 'w', 'w', 'w', 'w')]

filter_for will have a varying length of 1 to 7 so I using and is not going to be ideal.

I've tried using

[i for i in a if all(j in filtered_for for j in a)]

but that doesn't work.

EDIT: If ('1', '5', '5', '5', 'w', 'w', 'w', 'w') was also in the list I wouldn't want that tuple to be found. I guess I didn't specify this as all working solutions below would return this tuple as well.

Upvotes: 0

Views: 2807

Answers (4)

odrling
odrling

Reputation: 134

This code seems to work, it tests every list by dividing them in several lists of the same length as filter_for

Edit: I tried to add some excluded patterns after your edit

a = [('1', '2', '5', '5', 'w', 'w', 'w', 'w'),
     ('1', '3', '5', '5', 'w', 'w', 'w', 'w'),
     ('1', '3', '4', '5', 'w', 'w', 'w', 'w'),
     ('1', '4', '4', '4', 'w', 'w', 'w', 'w'),
     ('1', '5', '5', '5', 'w', 'w', 'w', 'w')]

filter_for = ['5', '5', 'w', 'w', 'w', 'w']
excluded = [('1', '5', '5', '5', 'w', 'w', 'w', 'w')]

# add a padding key to excluded patterns
for x in range(len(excluded)):
    value = excluded[x]
    excl = {'value': value}

    for i in range(len(value) - len(filter_for) + 1):
        if list(value[i:i+len(filter_for)]) == list(filter_for):
            excl['padding'] = (i, len(value) - i - len(filter_for))

    excluded[x] = excl


def isexcluded(lst, i):
    # check if the lst is excluded by one of the `excluded` lists
    for excl in excluded:
        start_padding, end_padding = excl['padding']

        # get start and end indexes
        start = max(i-start_padding, 0)
        end = min(i + len(excl['value']) + end_padding, len(lst))

        if list(lst[start:end]) == list(excl['value']):
            return True

    return False


def get_lists(lists, length, excluded):
    for lst in lists:
        # get all the 'sublist', parts of the list that are of the same
        # length as filter_for
        for i in range(len(lst)-length+1):
            tests = [list(lst[i:i+length]) == list(filter_for),
                     not isexcluded(lst, i)]

            if all(tests):
                yield lst

result = list(get_lists(a, len(filter_for), excluded))

print(result)  # python 2: print result

Upvotes: -1

ospahiu
ospahiu

Reputation: 3525

If I understand your requirements correctly, this should return the expected results. Here we convert the lists to strings, and use in to check for membership.

>>> a = [('1', '2', '5', '5', 'w', 'w', 'w', 'w'),
 ('1', '3', '5', '5', 'w', 'w', 'w', 'w'),
 ('1', '3', '4', '5', 'w', 'w', 'w', 'w'),
 ('1', '4', '4', '4', 'w', 'w', 'w', 'w')]
>>> filter_for = ''.join(['5', '5', 'w', 'w', 'w', 'w'])
>>> print [tup for tup in a if filter_for in ''.join(tup)]
[('1','2','5','5','w','w','w','w'), ('1','3','5','5','w','w','w','w')]

The below code has been updated to match exact sub-lists in the list of tuples. Instead of pattern matching like in the example above, we take a far different approach here.

We start off by finding the head and tail of the filter list. We then find the the indices of where the head and tail occur in tup (we must reverse tup to find the tail_index, as index returns only the first element matched). Using our indices pair, we can then slice that sublist spanning the distance between head and tail. If this sublist matches the filter, then we know that only that range exists in the search tuple.

def match_list(filter_list, l):
    results = []
    filter_for = tuple(filter_list)
    head = filter_for[0]
    tail = filter_for[-1]

    for tup in l:
        reverse_tup = tup[::-1]
        if head and tail in tup:
            try:
                head_index = tup.index(head)
                index_key = reverse_tup.index(tail)
                tail_index = -index_key if index_key else None
                if tup[head_index:tail_index] == filter_for:
                    results.append(tup)  # Prints out condition-satisfied tuples.
            except ValueError:
                continue
    return results

Sample output

 >>> a = [('1', '2', '5', '5', 'w', 'w', 'w', 'w'),
 ('1', '3', '5', '5', 'w', 'w', 'w', 'w'),
 ('1', '3', '4', '5', 'w', 'w', 'w', 'w'),
 ('1', '4', '4', '4', 'w', 'w', 'w', 'w'),
 ('1', '5', '5', '5', 'w', 'w', 'w', 'w')]  # <- Does not match!
 >>> filter_for = ['5', '5', 'w', 'w', 'w', 'w']
 >>> print match_list(filter_for, a)
 [('1','2','5','5','w','w','w','w'), ('1','3','5','5','w','w','w','w')]  

Upvotes: 2

Peter234
Peter234

Reputation: 1052

I'm not sure If I get the point what you're trying. But I would do it as following:

>>>[i for i in a if "".join(filter_for) in "".join(i)]
[('1', '2', '5', '5', 'w', 'w', 'w', 'w'), ('1', '3', '5', '5', 'w', 'w', 'w', 'w')]

Upvotes: 2

BPL
BPL

Reputation: 9863

Did you mean this

[i for i in a if all([j in i for j in filter_for])]

instead of your line?

[i for i in a if all(j in filter_for for j in a)]

Upvotes: 0

Related Questions