Replace elements in a list of lists python - python

I have a list of lists as follows:
list=[]
*some code to append elements to list*
list=[['a','bob'],['a','bob'],['a','john']]
I want to go through this list and change all instances of 'bob to 'b' and leave others unchanged.
for x in list:
for a in x:
if "bob" in a:
a.replace("bob", 'b')
After printing out x it is still the same as list, but not as follows:
list=[['a','b'],['a','b'],['a','john']]
Why is the change not being reflected in list?

Because str.replace doesn't work in-place, it returns a copy. As immutable objects, you need to assign the strings to elements in your list of lists.
You can assign directly to your list of lists if you extract indexing integers via enumerate:
L = [['a','bob'],['a','bob'],['a','john']]
for i, x in enumerate(L):
for j, a in enumerate(x):
if 'bob' in a:
L[i][j] = a.replace('bob', 'b')
Result:
[['a', 'b'], ['a', 'b'], ['a', 'john']]
More Pythonic would be to use a list comprehension to create a new list. For example, if only the second of two values contains names which need checking:
L = [[i, j if j != 'bob' else 'b'] for i, j in L]

You can try using a dictionary object of python
import numpy as np
L = [['a','bob'],['a','bob'],['a','john']]
dic = {'bob':'b'} # you can specify more changes here
new_list = [dic.get(n, n) for n in np.concatenate(L)]
print(np.reshape(new_list,[-1,2]).tolist())
Result is
[['a', 'b'], ['a', 'b'], ['a', 'john']]

I'm going to use a simple example, but basically x is another variable and isn't linked to the list element. You have to change the list element directly in order to alter the list.
l=[1,2,3,4]
for x in l:
x=x+1
This doesn't change the list
l=[1,2,3,4]
for i,x in enumerate(l):
l[i]=x+1
this changes the list

I might be a little to the party, but a more Pythonic way of doing this is using a map and a list comprehension. It can operate on a list of the list with any number of values.
l = [['a','bob'],['a','bob'],['a','john']]
[list(map(lambda x: x if x != 'bob' else 'b', i)) for i in l]
it gives you the desired output
[['a', 'b'], ['a', 'b'], ['a', 'john']]
The main idea is that the inner loop is iterating through the inner loop and using the simple lambda function to perform the replacement.
I hope that this helps anyone else who is looking out for something similar.

This is the case because you are only changing the temporary variable a.
list = [1,2,3]
for i in list:
i+=1
list will still be [1,2,3]
you have to edit the string based on its index in the list

Related

Python list for loop

This is a function that I saw to find the unique items in an array in order, I am new to python but this seemed very elegant.
unique_in_order = lambda l: [z for i, z in enumerate(l) if i == 0 or l[i - 1] != z]
How does this for loop exactly work.
z for i,z in enumerate(l)
enumerate(..) is a builtin function that takes as input an iterable object (l here) and generates a sequence of tuples containing the index and the element for each element.
So enumerate([1,4,2,5]) emits tuples like (0,1), (1,4), (2,2), (3,5). If you use a comma-separated list of identifiers in the head of the for loop, the tuple is untupled. So:
for i,z in enumerate([1,4,2,5]):
pass
will iterate four times, the first time i will be 0 and z 1; the next iteration i will be 1 and z 4; the next iteration i will be 2 and z 2; the next iteration i will be 3 and z 5.
Now your statement also contains some list comprehension, the first z in z for i,z in enumerate(l) means it will emit the z values. Notice furthermore that there is condition (the if part), so not all values will be emitted.
You should start with concept of list comprehensions in python to understand what does this lambda function do. In short it creates list of z elements that meet a condition on right side of statement.
Another important thing is builtin enumerate function that simply emits list of touples consisting of element and it's index.
enumerate() helps you to iterate over both the indices and the items of sequences at once.
Here is an example :
>>> l=['a','b','c']
>>> for index,value in enumerate(l):
print (index,value)
0 a
1 b
2 c
The solution you've posted is wrong and doesn't return unique elements as it only checks for duplicates on the previous item only (l[i-1]!=z).
To elaborate on what I meant, here is a test run :
>>> unique_in_order = lambda l: [z for i, z in enumerate(l) if i == 0 or l[i - 1] != z]
>>> l=[1,1,123,5,6,123]
>>> unique_in_order(l)
[1, 123, 5, 6, 123]
You can see that 123 occurs twice because it was tested only against its previous element 6.
Before I provide a simple solution, we need to be clear that we are finding unique items from a list in order or we are trying to get rid of duplicates entirely.
A simple and elegant solution would be to use list.count method. It returns the number of times an item occurs in the list.
>>> l=['a', 'a',2,5,6,'b', 'c', 'd', 'e','e',2,2,6]
>>> [x for x in l if l.count(x)<2]
[5, 'b', 'c', 'd']
If you did not meant to discard the duplicates entirely and instead wanted the list to have a single occurence of the duplicate items then you can do this :
>>> l=['a', 'a',2,5,6,'b', 'c', 'd', 'e','e',2,2,6]
>>> dups=set()
>>> [x for x in l if x not in dups and (dups.add(x) or True)]
['a', 2, 5, 6, 'b', 'c', 'd', 'e']

How to do this list/dict comprehension in python

I am creating a python dictionary as follows:
d= {i : chr(65+i) for i in range(4)}
Now output of d is {0: 'A', 1: 'B', 2: 'C', 3: 'D'}
I have an list of keys that I want to look up as follows:
l = [0, 1]
Now what I want to do is create another list which contains the values corresponding to these keys and I wanted to know if there was a pythonic way to do so using list or dict comprehensions.
I can do something as follows:
[x for x in d[0]]
However, I do not know how to iterate over the entries of my list in this setting. I tried:
[x for x in d[a] for a in l] # name 'a' not defined
[x for x in d[for a in l]] # invalid syntax
You want to iterate over l, so use for element in l. Then look stuff up in your dictionary in the left-hand side, in the value-producing expression:
[d[element] for element in l]
Note that a dictionary mapping consecutive integers starting at 0 to letters isn't all that efficient; you may as well make it a list:
num_to_letter = [chr(65 + i) for i in range(4)]
This still maps 0 to 'A', 1 to 'B', etc, but without a hashing step.

Remove list element without mutation

Assume you have a list
>>> m = ['a','b','c']
I'd like to make a new list n that has everything except for a given item in m (for example the item 'a'). However, when I use
>>> m.remove('a')
>>> m
m = ['b', 'c']
the original list is mutated (the value 'a' is removed from the original list). Is there a way to get a new list sans-'a' without mutating the original? So I mean that m should still be [ 'a', 'b', 'c' ], and I will get a new list, which has to be [ 'b', 'c' ].
I assume you mean that you want to create a new list without a given element, instead of changing the original list. One way is to use a list comprehension:
m = ['a', 'b', 'c']
n = [x for x in m if x != 'a']
n is now a copy of m, but without the 'a' element.
Another way would of course be to copy the list first
m = ['a', 'b', 'c']
n = m[:]
n.remove('a')
If removing a value by index, it is even simpler
n = m[:index] + m[index+1:]
There is a simple way to do that using built-in function :filter .
Here is ax example:
a = [1, 2, 3, 4]
b = filter(lambda x: x != 3, a)
If the order is unimportant, you can use set (besides, the removal seems to be fast in sets):
list(set(m) - set(['a']))
This will remove duplicate elements from your original list though
We can do it via built-in copy() function for list;
However, should assign a new name for the copy;
m = ['a','b','c']
m_copy=m.copy()
m_copy.remove('a')
print (m)
['a', 'b', 'c']
print(m_copy)
['b', 'c']
You can create a new list without the offending element with a list-comprehension. This will preserve the value of the original list.
l = ['a', 'b', 'c']
[s for s in l if s != 'a']
Another approach to list comprehension is numpy:
>>> import numpy
>>> a = [1, 2, 3, 4]
>>> list(numpy.remove(a, a.index(3)))
[1, 2, 4]
We can do it without using in built remove function and also without creating new list variable
Code:
# List m
m = ['a', 'b', 'c']
# Updated list m, without creating new list variable
m = [x for x in m if x != a]
print(m)
output
>>> ['b', 'c']
The question is useful as I sometimes have a list that I use throughout my given script but I need to at a certain step to apply a logic on a subset of the list elements. In that case I found it useful to use the same list but only exclude the needed element for that individual step, without the need to create a totally new list with a different name. For this you can use either:
list comprehension: say you have l=['a','b','c'] to exclude b, you can have [x for x in l if x!='b']
set [only if order is unimortant]: list(set(l) - set(['b'])), pay attention here that you pass 'b' as list ['b']

How to numerically sort string in list

I have a list inside of a list, and the inner list has strings of numbers (float) and words.
What I need to sort the list by, is in position list[0]. So for example,
list = [['8.34', 'a'],['3.55', 'c'],['5.92', 'b']]
I'm trying to sort the list numerically to look like
list = [['3.55', 'c'],['5.92', 'b'],['8.34', 'a']]
I've tried
sorted(list, key = float)
but I get an error message: 'float() argument must be a string or a number' and I've tried using lambda as well. Neither works. Could someone help please?
You can try passing a lambda function.:
sorted(my_list, key = lambda x : float(x[0]))
x will be an element of the list (which is also a list, because my_list is a list of lists), and float(x[0]) will return the float representation of the first element of that list.
Demo:
>>> my_list = [['8.34', 'a'],['3.55', 'c'],['5.92', 'b']]
>>> print sorted(my_list, key = lambda x : float(x[0]))
[['3.55', 'c'], ['5.92', 'b'], ['8.34', 'a']]
Note:
Don't use list as the name of a variable, because you will hide its built-in implementation.
Your list contains lists, so you cannot use float directly. You need to use a function that returns float value of the first item in each list.
>>> lis = [['8.34', 'a'],['3.55', 'c'],['5.92', 'b']]
>>> lis.sort(key=lambda x: float(x[0]))
>>> lis
[['3.55', 'c'], ['5.92', 'b'], ['8.34', 'a']]
This earlier answer can be used in your case also:
How to sort a list of lists by a specific index of the inner list?
from operator import itemgetter
list = [['8.34', 'a'],['3.55', 'c'],['5.92', 'b']]
print sorted(list, key=itemgetter(0))
gives the desired output:
[['3.55', 'c'], ['5.92', 'b'], ['8.34', 'a']]

Iterating over lists of lists in Python

I have a list of lists:
lst1 = [["(a)", "(b)", "(c)"],["(d)", "(e)", "(f)", "(g)"]]
I want to iterate over each element and perform some string operations on them for example:
replace("(", "")
I tried iterating over the list using:
for l1 in lst1:
for i in l1:
lst2.append(list(map(str.replace("(", ""), l1)))
I wanted the out result to be the same as original list of lists but without the parenthesis. Also, I am looking for a method in editing lists of lists and not really a specific solution to this question.
Thank you,
Edit:
Yes, you should use normal for-loops if you want to:
Preform multiple operations on each item contained in each sub-list.
Keep both the main list as well as the sub-lists as the same objects.
Below is a simple demonstration of how to do this:
main = [["(a)", "(b)", "(c)"], ["(d)", "(e)", "(f)", "(g)"]]
print id(main)
print id(main[0])
print id(main[1])
print
for sub in main:
for index,item in enumerate(sub):
### Preform operations ###
item = item.replace("(", "")
item = item.replace(")", "")
item *= 2
sub[index] = item # Reassign the item
print main
print
print id(main)
print id(main[0])
print id(main[1])
Output:
25321880
25321600
25276288
[['aa', 'bb', 'cc'], ['dd', 'ee', 'ff', 'gg']]
25321880
25321600
25276288
Use a nested list comprehension:
>>> lst1 = [["(a)", "(b)", "(c)"],["(d)", "(e)", "(f)", "(g)"]]
>>> id(lst1)
35863808
>>> lst1[:] = [[y.replace("(", "") for y in x] for x in lst1]
>>> lst1
[['a)', 'b)', 'c)'], ['d)', 'e)', 'f)', 'g)']]
>>> id(lst1)
35863808
>>>
The [:] will keep the list object the same.
I just did what you did, i used the fact that each element of a list can be assigned a new (or updated) value:
>>> lst1 = [["(a)", "(b)", "(c)"],["(d)", "(e)", "(f)", "(g)"]]
>>> for x in range(len(lst1)):
for y in range(len(lst1[x])):
lst1[x][y] = lst1[x][y].replace("(", "")
>>> lst1
[['a)', 'b)', 'c)'], ['d)', 'e)', 'f)', 'g)']]
EDIT
This is how you do it with the "real problem" that you mentioned in the comment:
a = [[(12.22, 12.122, 0.000)], [(1232.11, 123.1231, 0.000)]]
some_num = 10
for x in range(len(a)):
b = list(a[x][0])
for y in range(len(b)):
b[y] *= some_num
a[x] = tuple(b)
print(a)
OUTPUT:
[(122.2, 121.22, 0.0), (12321.099999999999, 1231.231, 0.0)]
^ All elements have been multiplied by a number and the original format is kept
This is how it works:
So you have the initial list 'a' that has two sublists each with only ONE element (the tuple that contains the x,y,z coordinates). I go through list 'a' and make the tuples a list and set them equal to 'b' (so the fourth line has a value of [12.22, 12.122, 0.000] the first time around (and the next tuple (as a list) the next time around).
Then I go through each of the elements in 'b' (the tuple converted into a list) and multiply each element in that tuple by a number with the use of the increment operator (+=, -=, /=, *=). Once this loop is done, I set that same position in the master list 'a' equal to the tuple of the previously converted tuple. < If this doesn't make sense, what I'm saying is that the initial tuples are converted into lists (then operated on), and then converter back to tuples (since you want it to end up with the same format as before).
Hope this helps!
>>> lst1 = [["(a)", "(b)", "(c)"],["(d)", "(e)", "(f)", "(g)"]]
>>> [[j.strip('()') for j in i] for i in lst1]
[['a', 'b', 'c'], ['d', 'e', 'f', 'g']]
>>> [[j.lstrip('(') for j in i] for i in lst1]
[['a)', 'b)', 'c)'], ['d)', 'e)', 'f)', 'g)']]

Categories