Create a list of integer elements from list of objects - python

I need to create list of non-duplicated integer elements from list of objects.
For example:
There is an object with two attributes: 'id' and 'other_id':
first = [elem.id for elem in objects_list]
second = [elem.other_id for elem in objects_list]
print first
[0,1,2,3,4,5]
print second
[4,5,6,7,9]
Now I can create two list containing that two attributes from all objects like this:
first = [elem.id for elem in objects_list]
first.extend(elem.other_id for elem in objects_list if elem.other_id not in first)
print first
[0,1,2,3,4,5,6,7,9]
Is there any way to do this in shorter way?

Use a set:
sorted(set().union(first, second)) #returns a sorted list of unique items
Demo:
>>> first = [0,1,2,3,4,5]
>>> second = [4,5,6,7,9]
>>> sorted(set(first + second))
[0, 1, 2, 3, 4, 5, 6, 7, 9]
If the original order matters:
>>> first = [0,1,2,3,4,5]
>>> seen = set(first)
>>> first += [x for x in second if x not in seen and not seen.add(x)]
>>> first
[0, 1, 2, 3, 4, 5, 6, 7, 9]
For large lists the set approach is going to be efficient as sets provide O(1) lookup, for tiny lists your approach is also okay.

Related

Difference Between two list of list [duplicate]

So heres my code:
item = [0,1,2,3,4,5,6,7,8,9]
z = [] # list of integers
for item in z:
if item not in z:
print item
z contains a list of integers. I want to compare item to z and print out the numbers that are not in z when compared to item.
I can print the elements that are in z when compared not item, but when I try and do the opposite using the code above nothing prints.
Any help?
Your code is not doing what I think you think it is doing. The line for item in z: will iterate through z, each time making item equal to one single element of z. The original item list is therefore overwritten before you've done anything with it.
I think you want something like this:
item = [0,1,2,3,4,5,6,7,8,9]
for element in item:
if element not in z:
print(element)
But you could easily do this like:
[x for x in item if x not in z]
or (if you don't mind losing duplicates of non-unique elements):
set(item) - set(z)
>> items = [1,2,3,4]
>> Z = [3,4,5,6]
>> print list(set(items)-set(Z))
[1, 2]
Using list comprehension:
print [x for x in item if x not in Z]
or using filter function :
filter(lambda x: x not in Z, item)
Using set in any form may create a bug if the list being checked contains non-unique elements, e.g.:
print item
Out[39]: [0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print Z
Out[40]: [3, 4, 5, 6]
set(item) - set(Z)
Out[41]: {0, 1, 2, 7, 8, 9}
vs list comprehension as above
print [x for x in item if x not in Z]
Out[38]: [0, 1, 1, 2, 7, 8, 9]
or filter function:
filter(lambda x: x not in Z, item)
Out[38]: [0, 1, 1, 2, 7, 8, 9]
list1 = [1,2,3,4]; list2 = [0,3,3,6]
print set(list2) - set(list1)
If you run a loop taking items from z, how do you expect them not to be in z? IMHO it would make more sense comparing items from a different list to z.
No, z is undefined. item contains a list of integers.
I think what you're trying to do is this:
#z defined elsewhere
item = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in item:
if i not in z: print i
As has been stated in other answers, you may want to try using sets.
>>> item = set([0,1,2,3,4,5,6,7,8,9])
>>> z = set([2,3,4])
>>> print item - z
set([0, 1, 5, 6, 7, 8, 9])
Your code is a no-op. By the definition of the loop, "item" has to be in Z. A "For ... in" loop in Python means "Loop though the list called 'z', each time you loop, give me the next item in the list, and call it 'item'"
http://docs.python.org/tutorial/controlflow.html#for-statements
I think your confusion arises from the fact that you're using the variable name "item" twice, to mean two different things.
You are reassigning item to the values in z as you iterate through z. So the first time in your for loop, item = 0, next item = 1, etc... You are never checking one list against the other.
To do it very explicitly:
>>> item = [0,1,2,3,4,5,6,7,8,9]
>>> z = [0,1,2,3,4,5,6,7]
>>>
>>> for elem in item:
... if elem not in z:
... print elem
...
8
9
In the case where item and z are sorted iterators, we can reduce the complexity from O(n^2) to O(n+m) by doing this
def iexclude(sorted_iterator, exclude_sorted_iterator):
next_val = next(exclude_sorted_iterator)
for item in sorted_iterator:
try:
while next_val < item:
next_val = next(exclude_sorted_iterator)
continue
if item == next_val:
continue
except StopIteration:
pass
yield item
If the two are iterators, we also have the opportunity to reduce the memory footprint not storing z (exclude_sorted_iterator) as a list.
If the lists are sorted and you know the elements of the checking list are in the base list - you can do a more optimal O(n) solution by using two pointers (where n will be the length of the base_list:
base_list = [0, 1, 2, 3, 4, 5, 6, 7, 8]
checking_list = [1, 3, 5]
expected_return = [0, 2, 4, 6, 7, 8]
j = 0
i = 0
elements_not_in_checking_list = []
while i < len(base_list):
if j < len(checking_list) and base_list[i] == checking_list[j]:
i += 1
j += 1
else:
elements_not_in_checking_list.append(base_list[i])
i += 1
Many of the solutions already posted here will not preserve the original ordering of the elements (because sets are unordered) or are inefficient (because linear search in a list is slower than a lookup in a set).
You can make a set of elements to remove upfront, and then use a list comprehension to retain only the elements which aren't in the set:
items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
z = [3, 4, 5, 6]
set_z = set(z)
result = [e for e in items if e not in set_z]
Then, result contains:
[0, 1, 2, 7, 8, 9]

When using range function to populate list why do I get list formatting error when printing with %d?

I'm learning python and I'm a bit puzzled by some behavior of lists when I create one using the range function. Hopefully someone can help me wrap my head around this? Here is a block of code I wrote:
elements = []
elements.append(range(1, 6))
for number in elements:
print "Line %d" % number
When I run this, I get the error for line 4:
TypeError: %d format: a number is required, not list
If I re-write the code as such:
elements = []
for i in range(1, 6):
elements.append(i)
for number in elements:
print "Line %d" % number
it runs how I expected the first block would.
If I replace the %d with a %r, it prints the list as
Line [1, 2, 3, 4, 5]
instead of printing each number on its own line.
When I directly append the range to the elements list is it somehow making a list of a list instead of iterated values like it does when using the for?
With a little testing I got this:
>>> print range(1, 6)
[1, 2, 3, 4, 5]
>>> elements = []
>>> elements.append(range(1, 6))
>>> print elements
[[1, 2, 3, 4, 5]]
Is this why it won't let me print the numbers using the %d formatter? How would I un-nest this list or access the contents of it? I'd be very grateful if someone could walk me through this. Thanks!
What's happening when you append a list to another list is that you'll get a nested list. Initially you had elements = []. In that list you appended another list (range(1, 6)) which makes it a list of a list.
>>> elements = []
>>> elements
[]
>>> elements.append(range(1, 6))
>>> elements
[[1, 2, 3, 4, 5]]
What you can do here is concatenate instead of appending by using the + operator (i.e. elements + range(1, 6).
>>> elements = []
>>> elements
[]
>>> elements + range(1, 6)
[1, 2, 3, 4, 5]
Because range(1,6) returns a list. So elements looks like:
[[1, 2, 3, 4, 5]] and the first iteration of your for loop number = elements[0] = [1, 2, 3, 4, 5], which is a list.
If you do this, you will get what you expect:
elements = []
elements = range(1, 6)
for number in elements:
print "Line %d" % numberr
In Python 2.7, range returns a list. And append method adds an item to the end of the list.
So, when you write elements.append(range(1, 6)), elements is now a list with a single element (that is a list).
You may want to use
elements.extend(range(1,6)) # 'extend' append all elements of the given list
Or directly
elements = range(1,6)

How do you clean up redundant values in a list of lists

Say I have a list of list
X=[[0,0,0,3,4],[8,8,9,2,8,2]]
How do I make it so that each sublist will only contain a repeated number once:
Like this new list:
XNew=[[0,3,4],[8,9,2]]
You can use set for that:
new_x = [list(set(i)) for i in old_x]
Sets are a collection of unique elements and therefore create a set of unique values when a list of duplicate values is cast as a set. You can then convert the set back to a list and get the desired result.
Example
>>> old_x = [[0,0,0,3,4],[8,8,9,2,8,2]]
>>> new_x = [list(set(i)) for i in old_x]
>>> print new_x
[[0,3,4],[8,9,2]]
If you need to keep the order of your numbers, you can't use sets. This would keep the original order:
lst = [[0, 0, 0, 3, 4], [8, 8, 9, 2, 8, 2]]
new_lst = []
for sub_lst in lst:
seen = set()
new_sub = []
for item in sub_lst:
if item not in seen:
new_sub.append(item)
seen.add(item)
new_lst.append(new_sub)
print new_lst # [[0, 3, 4], [8, 9, 2]]

Concatenate List Object Name with a Number and Retain the List Python

I'm using python 2.7 I'm trying to figure out a way to change the names of my lists automatically.
Let me explain i have multiple lists
list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 9, 3]
list3 = [8, 4, 3, 2, 1]
I would like to call the lists in a loop to determine which lists contain or do not contain a particular number.
My first thought was
x = "list" + str(i) # (where i iterates in the loop)
print x
However, using the above code only gave me the string "list1"(when i=1).
What I want is to be able to call the list that is named list1 and use the .count() operator to determine whether or not the number exists if it doesn't i want to call the next list until I'm out of lists(there will eventually be up to 30 lists).
Thanks,
Ryan
You shouldn't approach it like this. Put your lists in a container to iterate over them instead:
In [5]: for l in (list1, list2, list3):
...: print l.count(2)
...:
1
0
1
What you could do in a real-life use case is create a list of lists and fill it dynamically.
Then to get the first list that contains a given number, you could do:
In [6]: lists = [list1, list2, list3]
In [7]: next(l for l in lists if 9 in l)
Out[7]: [4, 5, 9, 3]
put the list in dict:
list1 = [1,2.4]
list2 = [2,5,6]
dlist = {1:list1,2:list2}
for k in dlist:
print dlist[k]

Finding elements not in a list

So heres my code:
item = [0,1,2,3,4,5,6,7,8,9]
z = [] # list of integers
for item in z:
if item not in z:
print item
z contains a list of integers. I want to compare item to z and print out the numbers that are not in z when compared to item.
I can print the elements that are in z when compared not item, but when I try and do the opposite using the code above nothing prints.
Any help?
Your code is not doing what I think you think it is doing. The line for item in z: will iterate through z, each time making item equal to one single element of z. The original item list is therefore overwritten before you've done anything with it.
I think you want something like this:
item = [0,1,2,3,4,5,6,7,8,9]
for element in item:
if element not in z:
print(element)
But you could easily do this like:
[x for x in item if x not in z]
or (if you don't mind losing duplicates of non-unique elements):
set(item) - set(z)
>> items = [1,2,3,4]
>> Z = [3,4,5,6]
>> print list(set(items)-set(Z))
[1, 2]
Using list comprehension:
print [x for x in item if x not in Z]
or using filter function :
filter(lambda x: x not in Z, item)
Using set in any form may create a bug if the list being checked contains non-unique elements, e.g.:
print item
Out[39]: [0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print Z
Out[40]: [3, 4, 5, 6]
set(item) - set(Z)
Out[41]: {0, 1, 2, 7, 8, 9}
vs list comprehension as above
print [x for x in item if x not in Z]
Out[38]: [0, 1, 1, 2, 7, 8, 9]
or filter function:
filter(lambda x: x not in Z, item)
Out[38]: [0, 1, 1, 2, 7, 8, 9]
list1 = [1,2,3,4]; list2 = [0,3,3,6]
print set(list2) - set(list1)
If you run a loop taking items from z, how do you expect them not to be in z? IMHO it would make more sense comparing items from a different list to z.
No, z is undefined. item contains a list of integers.
I think what you're trying to do is this:
#z defined elsewhere
item = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in item:
if i not in z: print i
As has been stated in other answers, you may want to try using sets.
>>> item = set([0,1,2,3,4,5,6,7,8,9])
>>> z = set([2,3,4])
>>> print item - z
set([0, 1, 5, 6, 7, 8, 9])
Your code is a no-op. By the definition of the loop, "item" has to be in Z. A "For ... in" loop in Python means "Loop though the list called 'z', each time you loop, give me the next item in the list, and call it 'item'"
http://docs.python.org/tutorial/controlflow.html#for-statements
I think your confusion arises from the fact that you're using the variable name "item" twice, to mean two different things.
You are reassigning item to the values in z as you iterate through z. So the first time in your for loop, item = 0, next item = 1, etc... You are never checking one list against the other.
To do it very explicitly:
>>> item = [0,1,2,3,4,5,6,7,8,9]
>>> z = [0,1,2,3,4,5,6,7]
>>>
>>> for elem in item:
... if elem not in z:
... print elem
...
8
9
In the case where item and z are sorted iterators, we can reduce the complexity from O(n^2) to O(n+m) by doing this
def iexclude(sorted_iterator, exclude_sorted_iterator):
next_val = next(exclude_sorted_iterator)
for item in sorted_iterator:
try:
while next_val < item:
next_val = next(exclude_sorted_iterator)
continue
if item == next_val:
continue
except StopIteration:
pass
yield item
If the two are iterators, we also have the opportunity to reduce the memory footprint not storing z (exclude_sorted_iterator) as a list.
If the lists are sorted and you know the elements of the checking list are in the base list - you can do a more optimal O(n) solution by using two pointers (where n will be the length of the base_list:
base_list = [0, 1, 2, 3, 4, 5, 6, 7, 8]
checking_list = [1, 3, 5]
expected_return = [0, 2, 4, 6, 7, 8]
j = 0
i = 0
elements_not_in_checking_list = []
while i < len(base_list):
if j < len(checking_list) and base_list[i] == checking_list[j]:
i += 1
j += 1
else:
elements_not_in_checking_list.append(base_list[i])
i += 1
Many of the solutions already posted here will not preserve the original ordering of the elements (because sets are unordered) or are inefficient (because linear search in a list is slower than a lookup in a set).
You can make a set of elements to remove upfront, and then use a list comprehension to retain only the elements which aren't in the set:
items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
z = [3, 4, 5, 6]
set_z = set(z)
result = [e for e in items if e not in set_z]
Then, result contains:
[0, 1, 2, 7, 8, 9]

Categories