Search code examples
pythonsetcomparisonnested-loops

Loop through list, if any of the index values in list has length < 3. Then append to new_list. Ignore indexes that repeat those elements anywhere else


Suppose, I have list called C.

C = (1,2,3),(4,5,6),(20),(14),(16,17,18,21),(21,22),(22,23),(24,25)

I would like to join values such as 20 and 14 into a new_list; only if 20, and 14 do not occur in any of the subsets such as (1,2,3),(16,17,18,21), etc. I wouldn't want to join (21,22) because (21) already exists in (16,17,18,21). Neither would I want to join (21, 22) or (22,23) because they repeat.

In short, I'm taking (24, 25) & (20) & (14) because they don't repeat in any of the subsets. Also, they must have less than 3 elements.

Example

new_list = (24,25,20,14)

This is what I tried so far.

new_list=[];
for a in range(0, len(C)):
    #suppose to prevent out of range error
    if a <= len(C) -1:
        #suppose to check every subset for repeating elements
        for b in range(len(C[a+1])):
            #again to prevent out of range error
            if b <= len(C) - 1:
                #using set comparison (this may be a semantic bug)
                false_or_true_trip_wire = set(str(C[a])) <= set(C[b])
                #I don't want to accidently append a duplicate
                I_do_not_want_both_sets_too_equal = set(str(C[a])) != set(C[b])
                if false_or_true_trip_wire == 'False':
                    if I_do_not_want_both_sets_too_equal == 'True':
                        new_list.append(C[a])
                        new_list.append(C[b])

Output

Type errors when any of the subsets have one element. It would work for subsets with 2 or more elements such as one's with len() of 3. Such as (1,2,3)

Example of one of the things I'm trying to do.

C = (5,6,2),(1,3,4),(4),(3,6,2)

for j in range(0, len(C)):
  print(len(C[j]))

Output from example

3
3
Traceback (most recent call last):
  File "C:/Users/User/Desktop/come on.py", line 4, in <module>
    print(len(C[j]))
TypeError: object of type 'int' has no len()

Question

So, is there any way to create a function that would do what I exemplified above? And, without using str()?

  • Please do give an explanation for someone who hasn't taken a class in Python, but has been self-teaching.

  • If I can find a function to get rid of all these nested loops it would make it so much easier to get rid of semantic bugs that cause type errors. Any answer would help.


Solution

  • Suppose, I have list called C.

    C = (1,2,3),(4,5,6),(20),(14),(16,17,18,21),(21,22),(22,23),(24,25)

    First of all, your C variable is assigned to a tuple of tuple and int objects not a list. See this tutorial for more info on these objects. You can also verify this is the case with your own code here.

    Type errors when any of the subsets have one element. It would work for subsets with 2 or more elements such as one's with len() of 3. Such as (1,2,3)

    You get TypeError because a tuple of a single object is not actually a tuple, it is just that object inside of it. Thus if you call len function on the nested object then the len function will raise such a TypeError should that nested object not be a sequence object with a __len__ method.

    int objects do not have this __len__ method and thus a TypeError: object of type 'int' has no len() is raised when they are passed into the len function. In the tuple you have assigned to C, you have two such int objects at index 2 (20) and 3 (14). To actually make these into tuple objects, you need to use a trailing comma to make what's called a singleton:

    C = (1,2,3),(4,5,6),(20,),(14,),(16,17,18,21),(21,22),(22,23),(24,25)
    
    for j in range(0, len(C)):
        # Now that every object in the tuple is another tuple len(C[j]) will work!
        print(len(C[j]))
        print(type(C[j]))
    
    

    See it work in python tutor!

    Now that is out of the way, I don't want to assume you want to change C from a tuple of tuple and int objects into just a tuple of tuple objects, so lets go back to your original C = (1,2,3),(4,5,6),(20),(14),(16,17,18,21),(21,22),(22,23),(24,25) and see if we can write some code that produces something like your expected (24,25,20,14) following the rules you outlined:

    In short, I'm taking (24, 25) & (20) & (14) because they don't repeat in any of the subsets. Also, they must have less than 3 elements.

    Here is the code I came up with that seems to follow those rules:

    C = (1,2,3),(4,5,6),(20),(14),(16,17,18,21),(21,22),(22,23),(24,25)
    temp_C = [x  if type(x) == tuple else tuple([x]) for x in C]
    new_c = []
    for i,c in enumerate(temp_C):
        l = len(c)
        if l <= 2:
            temp_b = [
                item for j, sub in enumerate(temp_C) for item in sub if i!=j
            ]
            if all(
                [y not in temp_b for y in c]
            ):
                [new_c.append(y) for y in c]
    
    new_c = tuple(new_c)
    
    print(new_c)
    
    

    outputs:

    (20, 14, 24, 25)
    

    It's not the same order as (24,25,20,14), but it's as close to your expected output as I'm going to get for you tonight. Finally, here is that code in python tutor. Hopefully, it's logic will become more clear to you as you step through it.