Python: create a list containing dicts - python

how would you turn this string:
str='ldap:alberthwang,eeid:67739|ldap:meng,eeid:107,building:CL5'
into a list that give you this:
print x[1]['building']=CL5
which would be:
x=[{'ldap':'alberthwang','eeid':'67739'},{'ldap':'meng','eeid':'107','building':'CL5'}]
i've tried to split the string first and append to a list:
sample=[]
for s in str.split('|'):
sample.append(s)
But i'm stuck on how to turn the list items into a dictionary that i can then use to populate another list.

text='ldap:alberthwang,eeid:67739|ldap:meng,eeid:107,building:CL5'
sample=[
dict(item.split(':') for item in part.split(','))
for part in text.split('|')]
print(sample)
# [{'eeid': '67739', 'ldap': 'alberthwang'}, {'building': 'CL5', 'eeid': '107', 'ldap': 'meng'}]
print(sample[1]['building'])
# CL5
List comprehensions are a very convenient way to construct
lists such as this.
A dict can be constructed from an iterable of key-value pairs. The iterable used above was a generator expression.
str is a built-in type, so assigning a string to str overwrites
the builtin. It's better to choose some other variable name to avoid
future surprising bugs.
I read and write list comprehensions backwards:
[ expression # (3)
for variable in # (2)
iterable # (1)
]
(1): First, understand the iterable. In the solution above, this is text.split('|').
(2): for variable in causes variable to be assigned to the values in iterable, one at a time.
(3): Finally, expression can be any Python expression, (usually) using variable.
The syntax for generator expressions is almost the same. The difference between a list comprehension and a generator expression is that a list comprehension returns a list, while a generator expression returns an iterator -- an object that yields its contents on-demand (as it is looped over, or when next is called) instead of generating all the items at once as is the case with lists.
A list can consume a lot of memory if the list is long.
A generator expression will consume less memory (and can even be infinite) because not all elements have to exist in memory at the same time.

Using str as a variable name is a bad idea, since it overshadows the built-in str.
s ='ldap:alberthwang,eeid:67739|ldap:meng,eeid:107,building:CL5'
res = [dict(colonStr.split(':') for colonStr in ds.split(','))
for ds in s.split('|')]
stores the result you what in res.

The key insight here is that dict can take a list of key/value pairs. So, you can do this using a comprehension like this:
string = 'ldap:alberthwang,eeid:67739|ldap:meng,eeid:107,building:CL5'
list_of_dicts = [dict(item.split(':') for item in items.split(',')) for items in string.split('|')]
print list_of_dicts

Maybe this way:
>>> s = 'ldap:alberthwang,eeid:67739|ldap:meng,eeid:107,building:CL5'
>>> x = [dict([d.split(':') for d in ls.split(',')]) for ls in s.split('|')]
>>> x[1]['building']
>>> 'CL5'

Related

Listing a valid and non-empty zip object prints empty list

zip_obj is a zip object containing 17292 tuples. A weird thing is happening with it:
sorted_zip_obj = sorted(zip_obj, key=lambda x: -abs(x[1]))
print(f'{len(list(zip_obj))} {len(sorted_zip_obj)}')
prints 0 17292. How come this happens? Why the first number printed is 0 and not 17292?
zip_obj is something that I am retrieving from somewhere else and unfortunately cannot share, and I cannot reproduce this behavior in small zip objects that I manually create.
If you're on python 3, zip_obj is probably a lazy zip object which you can iterate over only once. You've already exhausted it when you sorted it.
Try realizing it into a data structure like this:
zip_obj = tuple(zip_obj) # you can use `list` if you prefer
sorted_zip_obj = sorted(zip_obj, key=lambda x: -abs(x[1]))
print(f'{len(zip_obj)} {len(sorted_zip_obj)}') # removed the redundant `list`
before using it.
From the docs for zip:
Make an iterator that aggregates elements from each of the iterables.
Returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables.

Replace function in list of strings

I would like to iterate through a list of strings and replace each instance of a character ('1', for example) with a word. I am confused why this would not work.
for x in list_of_strings:
x.replace('1', 'Ace')
Side note, the strings within the lists are multiple characters long. ('1 of Spades)
You can use a list comprehension:
list_of_strings = [x.replace('1', 'Ace') for x in list_of_strings]
This is natural in Python. There is no significant benefit in changing your original list directly; both methods will have O(n) time complexity.
The reason your code does not work is str.replace does not work in place. It returns a copy, as mentioned in the docs. You can iterate over a range object to modify your list:
for i in range(len(list_of_strings)):
list_of_strings[i] = list_of_strings[i].replace('1', 'Ace')
Or use enumerate:
for idx, value in enumerate(list_of_strings):
list_of_strings[idx] = value.replace('1', 'Ace')

How do you create an empty list of tuples?

I'm trying to create a new empty list that will contain tuples when calling extend. Here's where I declare the list:
ticketData = list()
And I loop through another list to add tuples to this list:
data = (0, requestor, subject, thetime)
ticketData.extend(data)
When I output the result it shows this:
[0, 'Name', 'Test Subject', '03:31:12']
I need it to be a list of tuples, not just a list so that I can use sqlite's executemany function.
It seems straight forward, but I haven't been able to find an answer on here. Thanks!
Just append it to the list, so that the tuple will be added instead of having the elements of the tuple extend the list.
ticketData.append(data)
will add data to the list ticketData
Although this question has been answered already, I thought it could be useful to know about how to construct an iterable that should contain an empty iterable.
In Python, constructors like list(), set(), tuple() etc. take an iterable as input. Hence they iterate through the iterable and somthing like list(tuple()) does not return a list with an empty tuple. Instead the list constructor iterates through the tuples, and since it has no items in it it results in an empty list.
To construct a list that contains an empty tuple do the following:
Instead of my_list = list(tuple()) // returns [] or list()
Do: my_list = [tuple()]
Or: my_list = list([tuple()]) //constructor iterates through the list containing the empty tuple
Analogously, if you want to make a set containing an empty tuple do the following:
Instead of my_set = set(tuple()) // returns set()
Do: my_set = {tuple()}
Or: my_set = set([tuple()]) // constructor iterates through the list, containing the empty tuple
This also applies to other iterables. I am writing this, because I myself have had problems using these constructors to return e.g. a list containing another empty iterable.

Saving strings into list

I have a function which returns strings. What I would like to do is get these strings and save it into a list. How can I do this?
for i in objects:
string = getstring(i)
x = 0
list[x] = string
x = x+1
You should first declare the list:
L = []
then, in the for loop, you can append items to it:
for i in objects:
string = getstring(i)
L.append(string)
It is better to use a list comprehension in this case,
my_list = [getstring(obj) for obj in objects]
Instead of creating a list and storing string in it, we are creating a list of strings, based on the objects in objects. You can do the same with map function as well
my_list = map(getstring, objects)
This takes each and every item in objects and applies getstring function to them. All the results are gathered in a list. If you are using Python 3.x, then you might want to do
my_list = list(map(getstring, objects))
Since using map is not preferred, whenever possible go with List Comprehension. Quoting from the BDFL's blog,
Curiously, the map, filter, and reduce functions that originally motivated the introduction of lambda and other functional features have to a large extent been superseded by list comprehensions and generator expressions.

Editing elements in a list in python

How do I remove a character from an element in a list?
Example:
mylist = ['12:01', '12:02']
I want to remove the colon from the time stamps in a file, so I can more easily convert them to a 24hour time. Right now I am trying to loop over the elements in the list and search for the one's containing a colon and doing a substitute.
for num in mylist:
re.sub(':', '', num)
But that doesn't seem to work.
Help!
The list comprehension solution is the most Pythonic one, but, there's an important twist:
mylist[:] = [s.replace(':', '') for s in mylist]
If you assign to mylist, the barename, as in the other answer, rather than to mylist[:], the "whole-list slice", as I recommend, you're really doing something very different than "replacing entries in the list": you're making a new list and just rebinding the barename that you were previously using to refer to the old list.
If that old list is being referred to by multiple names (including entries in containers), this rebinding doesn't affect any of those: for example, if you have a function which takes mylist as an argument, the barename assignment has any effect only locally to the function, and doesn't alter what the caller sees as the list's contents.
Assigning to the whole-list slice, mylist[:] = ..., alters the list object rather than mucking around with switching barenames' bindings -- now that list is truly altered and, no matter how it's referred to, the new value is what's seen. For example, if you have a function which takes mylist as an argument, the whole-list slice assignment alters what the caller sees as the list's contents.
The key thing is knowing exactly what effect you're after -- most commonly you'll want to alter the list object, so, if one has to guess, whole-list slice assignment is usually the best guess to take;-). Performance-wise, it makes no difference either way (except that the barename assignment, if it keeps both old and new list objects around, will take up more memory for whatever lapse of time both objects are still around, of course).
Use a list comprehension to generate a new list:
>>> mylist = ['12:01', '12:02']
>>> mylist = [s.replace(':', '') for s in mylist]
>>> print mylist
['1201', '1202']
The reason that your solution doesn't work is that re.sub returns a new string -- strings are immutable in Python, so re.sub can't modify your existing strings.
for i, num in enumerate(mylist):
mylist[i] = num.replace(':','')
You have to insert the return of re.sub back in the list. Below is for a new list. But you can do that for mylist as well.
mylist = ['12:01', '12:02']
tolist = []
for num in mylist:
a = re.sub(':', '', num)
tolist.append(a)
print tolist
Strings in python are immutable, meaning no function can change the contents of an existing string, only provide a new string. Here's why.
See here for a discussion on string types that can be changed. In practice though, it's better to adjust to the immutability of strings.
Instead of list comprehension, you can also use a map call:
mylist = ['12:01', '12:02']
map(lambda f: f.replace(':', ''), mylist)
Returns:
['1201', '1202']

Categories

Resources