Check if item is member of list of objects - python

I'm looking for a clean way to check if an item is member of a list of objects.
Basically this is what I want to do:
class Constant:
def __init__(self,name,value):
self.name = name
self.value = value
list = [Constant('el1',1),Constant('el2',2)]
list2= ['el4','el5','el1']
for item in list2:
#clean solution for this if clause is needed (I'm aware list.name triggers an error)
if item in list.name:
print 'it is a member'
So it is important to me that the item matches only on the name, the value has no meaning when searching. I know I can solve this by adding an additional for loop like this:
for item in list2:
for itemConstant in list:
if item == itemConstant.name:
print 'it is a member'
But I want to be sure there is no better solution than this.

You can use any:
for item in list2:
if any(item == c.name for c in list):
print 'it is a member'

Since the value has no meaning use a set with in as strings are hashable and you will have a 0(1) lookups, storing the names from the instances in a set:
st = {Constant('el1',1).name,Constant('el2',2).name}
lst2 = ['el4','el5','el1']
for c in lst2:
if c in st:
print('it is a member')
Or make lst2 a set:
lst = [Constant('el1',1), Constant('el2',2)]
st = {'el4','el5','el1'}
for c in lst:
if c.name in st:
print(c.name)
I presume you want exact matches as "foo" in "foobar" would be True.
You can also leave the original list as is an create a set from the instance names:
lst = [Constant('el1', 1), Constant('el2', 2)]
lst2 = ['el4', 'el5', 'el1']
st = {c.name for c in lst}
for c in lst2:
if c in st:
print('it is a member')
So you still have an 0(n) solution as it just requires one more pass over your instances lst.

Make list2 a set and loop over your list of constants to test each against that set. Use the any() callable to exit early when a match is found:
constants = [Constant('el1', 1), Constant('el2',2)]
elements = {'el4', 'el5', 'el1'}
if any(c.name in elements for c in constants):
print 'is a member'
This reduces the problem to one loop; membership testing against a set is a O(1) constant time operation on average. The loop is exited early when a match is found, further reducing the number of tests made.

Related

Python: How to reference an object from the any() iterable

I'm trying to reference an object that I'm matching for.
import re
list = ["abc","b","c"]
if any(re.search(r"a",i) for i in list):
print("yes")
print(i)
This works, just not the last print command.
Is there any way to do what I'm trying do to here?
any only tells you whether anything fulfilled the condition, it doesn't let you have the value. The most pythonic way to do that is probably this:
try:
i = next(i for i in list if i == 'a')
print(i)
except StopIteration:
print('No such thing')
If you don't like the exception and would rather use an if:
i = next((i for i in list if i == 'a'), None)
if i:
print(i)
Variables from any() do not bleed out of it's scope - they are only known inside it.
You are just matching simple letters - you can get all items from your list that have this letter in them by using a list comprehension:
my_list = ["abc","b","c","abracadabra"]
with_a = [ item for item in my_list if "a" in item] # or re.find ... but not needed here
# this prints all of them - you can change it to if ...: and print(with_a[0])
# to get only the first occurence
for item in with_a:
print("yes")
print(item)
Output:
yes
abc
yes
abracadabra

Uniqueify returning a empty list

I'm new to python and trying to make a function Uniqueify(L) that will be given either a list of numbers or a list of strings (non-empty), and will return a list of the unique elements of that list.
So far I have:
def Uniquefy(x):
a = []
for i in range(len(x)):
if x[i] in a == False:
a.append(x[i])
return a
It looks like the if str(x[i]) in a == False: is failing, and that's causing the function to return a empty list.
Any help you guys can provide?
Relational operators all have exactly the same precedence and are chained. This means that this line:
if x[i] in a == False:
is evaluated as follows:
if (x[i] in a) and (a == False):
This is obviously not what you want.
The solution is to remove the second relational operator:
if x[i] not in a:
You can just create a set based on the list which will only contain unique values:
>>> s = ["a", "b", "a"]
>>> print set(s)
set(['a', 'b'])
The best option here is to use a set instead! By definition, sets only contain unique items and putting the same item in twice will not result in two copies.
If you need to create it from a list and need a list back, try this. However, if there's not a specific reason you NEED a list, then just pass around a set instead (that would be the duck-typing way anyway).
def uniquefy(x):
return list(set(x))
You can use the built in set type to get unique elements from a collection:
x = [1,2,3,3]
unique_elements = set(x)
You should use set() here. It reduces the in operation time:
def Uniquefy(x):
a = set()
for item in x:
if item not in a:
a.add(item)
return list(a)
Or equivalently:
def Uniquefy(x):
return list(set(x))
If order matters:
def uniquefy(x):
s = set()
return [i for i in x if i not in s and s.add(i) is None]
Else:
def uniquefy(x):
return list(set(x))

append/insert a value/string to a list element dynamically in Python?

my_list = ['cat','cow','dog','rabbit']
but what I want is to append/insert a character(or string) to one or more element(not all).
something like
my_list = ['cat','cow_%s','dog_%s','rabbit']%('gives milk','bark')
now updated list should look like
my_list = ['cat','cow_gives milk','dog_bark','rabbit']
one way is to do this is manually change/update the element one by one
e.g my_list[2]=my_list[2]+"bark"
but I don't want that because my_list is long(around 100s element ) and 40+ need to be changed dynamically.
In my case It is like
my_list = ['cat','cow_%s','dog_%s','rabbit']
for a in xyz: #a is a string and xyz is a list of string
my_list=my_list%(a,a+'b')
fun(my_list)
You could do something like this:
changes = {"cow":"gives milk", "dog":"bark"}
my_list = [item if item not in changes else "_".join([item, changes[item]])
for item in my_list]
For greater efficiency if you will do this repeatedly, as JAB suggests in the comments, build a dictionary to map items to their locations (indices) in the list:
locations = dict((s, i) for i, s in enumerate(my_list))
You can then use this to find the corresponding items.
If you are stuck with the list of strings, some ending "%s", and list of things to put in them, I guess you will have to do something like:
for i, s in enumerate(my_list): # work through items in list with index
if s.endswith("%s"): # find an item to update
my_list[i] = s % xyz.pop(0) # update with first item popped from xyz
Note that xyz will have to be a list for this, not tuple, as you can't pop items from a tuple.
If you have a replacement string in source strings, and an iterable of replacements, then you can do something such as:
import re
def do_replacements(src, rep):
reps = iter(rep)
for item in src:
yield re.sub('%s', lambda m: next(reps), item)
replaces = ('gives milk','bark', 'goes walkies', 'eat stuff')
my_list = ['cat','cow_%s','dog_%s_and_%s','rabbit_%s']
print list(do_replacements(my_list, replaces))
# ['cat', 'cow_gives milk', 'dog_bark_and_goes walkies', 'rabbit_eat stuff']
If you don't have enough replacements you'll get a StopIteration - you can either consider than an error, or alternatively provide a default (possibly empty) replacement to the replacement: lambda m: next(reps, '') for instance...
Here are two more possibilities: Either, you could create a list of verbs alongside your list of animals, with None for animals that shall not have a verb, and merge them together...
animals = ['cat','cow','dog','rabbit']
verbs = [None, 'gives milk', 'barks', None]
def combine(animal, verb):
return (animal + "_" + verb) if verb else animal
print map(combine, animals, verbs)
... or, if the animals already have those %s placeholders for verbs, iterate the animals, check if the current animal has a placeholder, and if so, replace it with the next verb. (Similar to Jon's answer, but using % instead of re.sub)
animals = ['cat','cow_%s','dog_%s','rabbit']
verbs = iter(['gives milk', 'barks'])
print [animal % next(verbs) if '%' in animal else animal
for animal in animals]

dealing with an empty list inside a list

I have a list which looks something like this:
mylist = ([(0.1, 0.5),(0.4, 1.0)], [(0.2, 0.4),(0.15, 0.6)], None, [(0.35, 0.8),(0.05, 1.0)])
What I would like to know is how do I check for the empty entry or None in the list and if there is one then it should continue further ignoring it. Something like,
if mylist == something :
do this
if mylist == [] or () or None :
ignore and continue
I am not able to put that into a code. Thank you.
Basically, in python
[], (), 0, "", None, False
all of these means that the value is False
Therefore:
newList = [i for i in myList if i] # this will create a new list which does not have any empty item
emptyList = [i for i in myList if not i] # this will create a new list which has ONLY empty items
or as you asked:
for i in myList:
if i:
# do whatever you want with your assigned values
else:
# do whatever you want with null values (i.e. [] or () or {} or None or False...)
and then you can do whatever you want with your new list :)
for sublist in mylist:
if sublist is None:
#what to do with None
continue
elif not sublist and isinstance(sublist, list):
#what to do if it's an empty list
continue
elif not isinstance(sublist, list):
#what to do if it's not a list
continue
#what to do if it's a list and not empty
Alternatively, you could leave out the 'continues' and put the general case in an else clause, only check for some of the possible circumstances, or nest the ifs.
Generally, if you knew you'd only get None or a container, just if not sublist: continue is adequate to ignore empty containers and None. To filter these values out of the list, do
mylist = [sublist for sublist in mylist if sublist]
Edit: You can't do this in the update function. You should pre-filter the list. Where you have
mylist = oldlist[:]
replace it with
mylist = [sublist for sublist in oldlist if sublist]
If the row name a, b, or whatever is there, but the rest is empty / None, then do
mylist = [sublist for sublist in oldlist if sublist[1]]
and this will filter on the truth value of the 2nd item intead of the first item / row title.
I will just do this:
for x in mylist:
if not x:
continue
#--> do what you want to do
but I have to say the first answer with comprehension list is more clean unless you need to do a complicated stuff inside the for statement.
How about this piece of code:
for x in mylist:
if x is None or x == [] or x == ():
continue
else:
do this

How to check if a list is contained inside another list without a loop?

Is there any builtins to check if a list is contained inside another list without doing any loop?
I looked for that in dir(list) but found nothing useful.
Depends on what you mean by "contained". Maybe this:
if set(a) <= set(b):
print("a is in b")
Assuming that you want to see if all elements of sublist are also elements of superlist:
all(x in superlist for x in sublist)
You might want to use a set
if set(a).issubset(b):
print('a is contained in b')
the solution depends on what values you expect from your lists.
if there is the possiblity of a repetition of a value, and you need to check that there is enough values in the tested container, then here is a time-inefficient solution:
def contained(candidate, container):
temp = container[:]
try:
for v in candidate:
temp.remove(v)
return True
except ValueError:
return False
test this function with:
>>> a = [1,1,2,3]
>>> b = [1,2,3,4,5]
>>> contained(a,b)
False
>>> a = [1,2,3]
>>> contained(a,b)
True
>>> a = [1,1,2,4,4]
>>> b = [1,1,2,2,2,3,4,4,5]
>>> contained(a,b)
True
of course this solution can be greatly improved: list.remove() is potentially time consuming and can be avoided using clever sorting and indexing. but i don't see how to avoid a loop here...
(anyway, any other solution will be implemented using sets or list-comprehensions, which are using loops internally...)
If you want to validate that all the items from the list1 are on list2 you can do the following list comprehension:
all(elem in list1 for elem in list2)
You can also replace list1 and list2 directly with the code that will return that list
all([snack in ["banana", "apple", "lemon", "chocolate", "chips"] for snack in ["chips","chocolate"])
That any + list comprehension can be translated into this for a better understanding of the code
return_value = False
for snack in snacks:
if snack in groceries:
return_value = True
else:
return_value = False

Categories