views:

714

answers:

6

in my list:

animals =  [ ['dog', ['bite'] ],
             ['cat', ['bite', 'scratch'] ],
             ['bird', ['peck', 'bite'] ], ]

add('bird', 'peck')
add('bird', 'screech')
add('turtle', 'hide')

The add function should check that the animal and action haven't been added before adding them to the list. Is there a way to accomplish this without nesting a loop for each step into the list?

+6  A: 

You're using the wrong data type. Use a dict of sets instead:

def add(key, value, userdict):
    userdict.setdefault(key, set())
    userdict[key].add(value)

Usage:

animaldict = {}
add('bird', 'peck', animaldict)
add('bird', 'screech', animaldict)
add('turtle', 'hide', animaldict)
recursive
If userdict is a collections.defaultdict(set) then you can omit the call to setdefault -- easier to understand IMHO.
John Fouhy
setdefault returns the existing or created value. You can use userdict.setdefault(key,set()).add(value), or save it to a variable for readability.
MizardX
A: 
animals_dict = dict(animals)

def add(key, action):
    animals_dict.setdefault(key, [])
    if action not in animals_dict[key]:
        animals_dict[key].append(action)

(Updated to use setdefault - nice one @recursive)

elo80ka
+4  A: 

While it is possible to construct a generic function that finds the animal in the list using a.index or testing with "dog" in animals, you really want a dictionary here, otherwise the add function will scale abysmally as more animals are added:

animals = {'dog':set(['bite']),
           'cat':set(['bite', 'scratch'])}

You can then "one-shot" the add function using setdefault:

animals.setdefault('dog', set()).add('bite')

It will create the 'dog' key if it doesn't exist, and since setdefault returns the set that either exists or was just created, you can then add the bite action. Sets ensure that there are no duplicates automatically.

Jarret Hardie
A: 

You really should use a dictionary for this purpose. Or alternatively a class Animal.

You could improve your code like this:

if not any((animal[0] == "bird") for animal in animals):
    # append "bird" to animals
Georg
+4  A: 

Based on recursive's solution, in Python 2.5 or newer you can use the defaultdict class, something like this:

from collections import defaultdict

a = defaultdict(set)

def add(animal, behavior):
    a[animal].add(behavior)

add('bird', 'peck')
add('bird', 'screech')
add('turtle', 'hide')
David Zaslavsky
A: 

While I agree with the others re. your choice of data structure, here is an answer to your question:

def add(name, action):
    for animal in animals:
        if animal[0] == name:
            if action not in animal[1]:
                animal[1].append(action)
            return
    else:
        animals.append([name, [action]])

The for loop is an inevitable consequence of your data structure, which is why everyone is advising you to consider dictionaries instead.

John Fouhy