having trouble with lists in python - python

The function satisfiesF() takes a list L of strings as a paramenter. function f takes a string as a parameter returns true or false. Function satisfiesF() modifies L to contain only those strings,s for which f(s) returns true.
I have two different programs aimed to produce the same output. But I am getting different outputs.
First program:
def f(s):
return 'a' in s
def satisfiesF(L):
k=[]
for i in L:
if f(i)==True:
k.append(i)
L=k
print L
print
return len(L)
L = ['a', 'b', 'a']
print satisfiesF(L)
print L
Output:
['a', 'a']
2
['a', 'b', 'a']
Second program:
def f(s):
return 'a' in s
def satisfiesF(L):
for i in L:
if f(i)==False:
L.remove(i)
print L
print
return len(L)
L = ['a', 'b', 'a']
print satisfiesF(L)
print L
output:
['a', 'a']
2
['a', 'a']
Please explain why these are giving differnt outputs.

In your second function you are seeing 2 as the length and all the elements in L outside the function because you are setting a local variableL which is a reference to k, your L created outside the function is not affected. To see the change in L you would need to use L[:] = k, then printing L will give you ['a', 'a'] outside the function as you are changing the original list object L list passed in to the function.
In the first you are directly modifying L so you see the changes in L outside the function.
Also never iterate over a list you are removing element from, if you make
L = ['a', 'b', 'a','a','d','e','a'], you will get behaviour you won't expect. Either make a copy for i in L[:] or use reversed for i in reversed(L):

In the first function, you assign over L in satisfiesF(), but you never modify the original list. When you write L=k, that makes the reference L now refer to the same list as k. It doesn't assign to the original list.
In contrast, in the second function you modify L without reassigning to it.
Also, as a side note, you shouldn't modify a list while you iterate over it.
As a second side note, you can rewrite satisfiesF as a one-liner:
L = [item for item in L if f(item)]

This was down voted mistakenly. The question was changed. So, the answer got outdated. Following is the answer for changed question:
L=k
Above would mean that we lost the reference to L.
So, Try this:
To the 1st program, comment the above assignment, do below, to retain reference to L:
# L=k
del L[:]
L[:] = k
Now both programs will output same, below one:
['a', 'a']
2
['a', 'a']
Best of luck.

In the question, there are two Ls. A global one and a local one. The
print L
statement prints the GLOBAL L, which you did not mutate in the programme.
Therefore, in order to let the programme knows that you want to mutate the global L, instead of the local L, you can add the line
globals()['L'] = L
to your first programme. I hope this can help!

In the first program, if you want to mutate the original list L and see the change made by your function, you should replace L = K in your code with L[:] = k:
def satisfiesF(L):
k=[]
for i in L:
if f(i)==True:
k.append(i)
# new code --------
L[:] = k # before: L = K
# -----------------
print L
print
return len(L)
This will give you ['a', 'a'] outside the function.
About mutating a list within a loop in the second program...
Just to remember that during a "for" loop, python keeps track of where it is in the list using an internal counter that is incremented at the end of each iteration.
When the value of the counter reaches the current length of the list, the loop terminates. This means that if you are mutating the list within the loop you can have surprising consequence.
For example, look at the for loop below:
my_list = ['a', 'b', 'c', 'd']
>>>print "my_list - before loop: ", my_list
my_list - before loop: ['a', 'b', 'c', 'd']
for char in my_list:
if char == 'a' or char == 'b':
my_list.remove(char)
>>>print "my_list - after loop: ", my_list
my_list - after loop: ['b', 'c', 'd']
Here, the hidden counter starts out at the index 0, discovers that "a" (in my_list[0]) is in the list, and remove it, reducing the length of my_list to 3 (from the initial 4). The counter is then incremented to 1, and the code proceeds to check the "if" condition, at the position my_list[1], in the mutated list (now of len == 3). This means that you will skip the "b" character (present now at the index 0) even if it had to be remove it.
One solution for this is to use slicing to clone and create a new list where you can remove items from it:
cloneList = my_list[:]

Related

New list of not repeated elements

I want to create a function that take a lsit as argument, for example:
list = ['a','b','a','d','e','f','a','b','g','b']
and returns a specific number of list elements ( i chose the number) such that no number occurs twice. For example if i chose 3:
new_list = ['a','b','d']
I tried the following:
def func(j, list):
new_list=[]
for i in list:
while(len(new_list)<j):
for k in new_list:
if i != k:
new_list.append(i)
return new_list
But the function went through infinite loop.
def func(j, mylist):
# dedup, preserving order (dict is insertion-ordered as a language guarantee as of 3.7):
deduped = list(dict.fromkeys(mylist))
# Slice off all but the part you care about:
return deduped[:j]
If performance for large inputs is a concern, that's suboptimal (it processes the whole input even if j unique elements are found in first j indices out of an input where j is much smaller than the input), so the more complicated solution can be used for maximum efficiency. First, copy the itertools unique_everseen recipe:
from itertools import filterfalse, islice # At top of file, filterfalse for recipe, islice for your function
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
now wrap it with islice to only pull off as many elements as required and exiting immediately once you have them (without processing the rest of the input at all):
def func(j, mylist): # Note: Renamed list argument to mylist to avoid shadowing built-in
return list(islice(unique_everseen(mylist), j))
Try this.
lst = ['a','b','a','d','e','f','a','b','g','b']
j = 3
def func(j,list_):
new_lst = []
for a in list_:
if a not in new_lst:
new_lst.append(a)
return new_lst[:j]
print(func(j,lst)) # ['a', 'b', 'd']
I don't know why someone does not post a numpy.unique solution
Here is memory efficient way(I think 😉).
import numpy as np
lst = ['a','b','a','d','e','f','a','b','g','b']
def func(j,list_):
return np.unique(list_).tolist()[:j]
print(func(3,lst)) # ['a', 'b', 'd']
list is a reserved word in python.
If order of the elements is not a concern then
def func(j, user_list):
return list(set(user_list))[:j]
it's bad practice to use "list" as variable name
you can solve the problem by just using the Counter lib in python
from collections import Counter
a=['a','b','a','d','e','f','a','b','g','b']
b = list(Counter(a))
print(b[:3])
so your function will be something like that
def unique_slice(list_in, elements):
new_list = list(Counter(list_in))
print("New list: {}".format(new_list))
if int(elements) <= len(new_list):
return new_list[:elements]
return new_list
hope it solves your question
As others have said you should not Shadow built-in name 'list'. Because that could lead to many issues. This is a simple problem where you should add to a new list and check if the element was already added.
The [:] operator in python lets you separate the list along an index.
>>>l = [1, 2, 3, 4]
>>>l[:1]
[1]
>>>l[1:]
[2, 3, 4]
lst = ['a', 'b', 'a', 'd', 'e', 'f', 'a', 'b', 'g', 'b']
def func(number, _list):
out = []
for a in _list:
if a not in out:
out.append(a)
return out[:number]
print(func(4, lst)) # ['a', 'b', 'd', 'e']

I don't understand that part of code about recursive (python)

I don't understand recursive part.
def length(list1):
if list1 == [] or list1 == "":
return 0
else:
return 1+length(list1[1:])
a = [1,2,3,4,5]
print "Length of list is: ",length(a)
This is code. and that code's role is like len function.
and output is for a; 5
but return 1+length(list1[1:]) that part is not understandable for me.
why there is "1"? and why it starts with 1? why not "0"?
I thought length(list1[0:]) but it doesn't run.
actually i think but, ı understand nothing about that part.
can anyone explain?
The length of a list can be defined recursively as:
0 if the list is empty (equal to the empty list [])
1 + the length of the list deprived of its first element. (e.g. if you have a list with 3 elements, the length is the same as 1 + the length of the same list without one of its element (2))
Here, list1[1:] is a new list similar to list1 but without the first element.
If we take a concrete example with the list ['a', 'b', 'c']:
['a', 'b', 'c'] is different than the empty list [], then we return 1 + length(['b', 'c']). ['b', 'c'] is different than the empty list [], then length(['b', 'c']) returns 1 + length(['c']); and so on until length([]) is called.
In python: a[1:] means "take all values of a starting from 1, so excluding 0", for instance:
"abc"[1:] == "bc"
[2, 3, 4][1:] == [3, 4]
The function you are looking at takes either a list or a string, then remove an item and counts the remaining until it find an empty one, in this case returns its length (0).

Python list unroll in a recursive function

The following is my python recursive function code:
char_list=[]
def xyz(sample_string):
global char_list
if sample_string!="":
char_list.append(sample_string[0])
xyz(sample_string[1:])
# once sample_string == "", the function will start unrolling
# While unrolling, I wanted to do certain operations on the char_list elements and other operations which includes recursive calls to function xyz()
for eg, if the initial value of sample_string is ABC, What I want is for each iteration char_list will be adding,
iter 1 - char_list = ['A']
iter 2 - char_list = ['A','B']
iter 3 - char_list = ['A','B','C']
Then sample_string will become empty and the function will start unrolling:
Now I want to get the list in each iteration in the following order, i.e.,
char_list = ['A','B','C']
char_list = ['A','B']
char_list = ['A']
so that I can do my further operations on the list. I can't use return keyword since there are other operations following in the same function including recursive calls to xyz().
The code I wrote is not serving that purpose. For each unroll, it is returning the full list (['A','B','C']), not in that decrement fashion. I tried the same with a local list variable, still not working. Any idea on how to do this ?
def xyz(sample_string):
l = list(sample_string)
if l :
for i in reversed(range(1, len(l)+1)):
print(l[0:i])
change the print to yield, and the function become a generator
what you need is list of number [1,2,3,4....], than you can use list_[0:i] to get slice of the list_, once you get the list of number, you can reverse it or do something else.
out:
['A', 'B', 'C']
['A', 'B']
['A']

How to force Python to correctly identify list element index in FOR loop

There is a simple list, for example,
my_list = ['a', 'b', 'b', 'c', 'c']
I want to run through my_list[1:] with for loop to get previous element value for each iteration:
for i in my_list[1:]:
print(my_list[my_list.index(i)-1])
I expected to see a b b c on output, but get a a b b instead.
I guess this is because index() method search only for first i value, ignoring the fact that there are two elements "b" as well as two "c"...
How to fix this code to get expected output?
The list.index() method will return the index of first occurrence of its argument. And since you have multiple duplicate items in your list it doesn't give you the expected result. You can use a simple slicing to get your expected output:
>>> my_list = ['a', 'b', 'b', 'c', 'c']
>>>
>>> my_list[:-1]
['a', 'b', 'b', 'c']
Or if you want to access these items through a loop you can use zip() function like following:
>>> for i, j in zip(my_list,my_list[1:]):
... print(i, j)
...
a b
b b
b c
c c
Matching elements with their predecessors or sucessors is a common use case for zip:
In [13]: for i,prior in zip(my_list[1:], my_list[0:]):
print (prior)
....:
a
b
b
c
You can always emulate the behaviour of C/Pascal/Perl/whatever 'for' instead of Python 'for' (which is actually more like foreach). Note that the range starts with 1 to avoid returning the last element on the first iteration.
for i in range(1, len(my_list)):
print(my_list[i], my_list[i-1])
Not very Pythonic, but this approach is sometimes more intuitive for people with background in other languages.
As you noticed, using index does not work here, as it always finds the first position of the given element. Also, it is pretty wasteful, as in the worst case you have to search the entire list each time.
You could use enumerate with start parameter to get the element along with its index:
start = 1
for i, x in enumerate(my_list[start:], start):
print(i, x, my_list[i-1]) # index, current, last
This will do the trick:
for i in range(len(my_list)+1):
try: print(my_list[i-1])
except: print 'it is 1st iteration'

Remove element in list using list comprehension - Python

I have a list like this:
['A','B','C']
What I need is to remove one element based on the input I got in the function. For example, if I decide to remove A it should return:
['B','C']
I tried with no success
list = ['A','B','C']
[var for var in list if list[var] != 'A']
How can I do it?
Thanks
Simple lst.remove('A') will work:
>>> lst = ['A','B','C']
>>> lst.remove('A')
['B', 'C']
However, one call to .remove only removes the first occurrence of 'A' in a list. To remove all 'A' values you can use a loop:
for x in range(lst.count('A')):
lst.remove('A')
If you insist on using list comprehension you can use
>>> [x for x in lst if x != 'A']
['B', 'C']
The above will remove all elements equal to 'A'.
The improvement to your code (which is almost correct) would be:
list = ['A','B','C']
[var for var in list if var != 'A']
However, #frostnational's approach is better for single values.
If you are going to have a list of values to disallow, you can do that as:
list = ['A','B','C', 'D']
not_allowed = ['A', 'B']
[var for var in list if var not in not_allowed]
If you not sure whether the element exists or not, you might want to check before you delete:
if 'A' in lst:
lst.remove('A')
Find this simplified code:
list1 = [12,24,35,24,88,120,155]
while 24 in list1:
list1.remove(24)
print(list1)
Best Luck!
originalList=['A','B','C']
print([val for val in originalList if val!='A'])
This prints
['B', 'C']
You can just use the remove method of list. Just do list.remove('A') and it will be removed.
If you have the index of the item to be removed, use the pop method. list.pop(0).
You were really close:
list = ['A','B','C']
[var for var in list if list[list.index(var)] != 'A']
You tried to refer to a list item using a syntax that calls for an index value.

Categories

Resources