简体   繁体   中英

Ensure a only single member of a sublist is present in a Python list

Let's say I have a Python list that might contain any combination of members from the following two tuples:

legal_letters = ('a', 'b', 'c')
legal_numbers = (1, 2, 3)

So legal combination lists would include

combo1 = ['a', 1, '3']
combo2 = ['c']
combo3 = ['b', 2, 1, 'c']

Any length, any combination. You can assume no duplicated characters will be in the combination list though. I'd like to apply a function to those combinations that modifies them (in place) such that they contain at most a single member of one of the tuples -- say it's numbers. The 'chosen' member of the number tuple should be selected at random. I also don't care if order gets mangled in the process.

def ensure_at_most_one_number(combo):
 # My first attempts involved set math and a while loop that was 
 # pretty gross, I'll spare you guys the details.  I'm sure I could get it to work
 # but I figured there might be a one-liner or some fancy itertools out there
 return combo


# Post transformation
combo1 = ['a', '1']
combo2 = ['c']
combo3 = ['c', 'b', 2] # Mangled order, not a problem

I can't think of any one-liner to solve this, but I believe this is concise enough.

    def only_one_number(combo):
        import random

        try:
            number = random.choice([x for x in combo if x in legal_numbers])
            combo[:] = [x for x in combo if x in legal_letters]
            combo.append(number)
        except IndexError:
            pass

In case you don't instantly see the need for the exception handling, we need to catch the IndexError that would result from trying to pass an empty list to random.choice().

Not the best, but it should work

numbers = []
for i in legal_numbers:
  if i in combo:
    numbers.append(i)
    combo.remove(i)
if len(numbers) == 0:
  return combo
combo.append(random.choice(numbers))
return combo

Maybe This?

def ensure_at_most_one_number(combo):
    i = len(combo) - 1      # start with the last element
    found_number = False

    while i >= 0:
        try:
            int(combo[i])   # check element is a number
            if  found_number == True:
                del combo[i]    # remove it if a number already found
            else:
                found_number = True
        except ValueError:
            pass             # skip element if not a number
        i -= 1

    return combo

Disclaimer: I'm a python beginner myself, so there might be a better way, but this is how I would do it :

import random

def ensure_at_most_one_number(combo, legal_numbers) :

    random.shuffle(combo)
    first_number = True

    for i in range(len(combo)-1, -1, -1) :
        if combo[i] in legal_numbers :
            if first_number :
                first_number = False
            else :
                del combo[i]

Note that I shuffle the list since you said you wanted to keep a random element. The iteration is done backwards to keep the correct indices after elements have been deleted from the list.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM