Attribute error while using functools.reduce - python-3.x

I get an attribute error while running this code. Why isn't the result.append(item) being returned in the next iteration?
from functools import reduce
def reducer(arr):
return reduce((lambda result,item: result.append(item)),arr,[])
print(reducer([[1,3],[2,4]]))

list.append doesn't return the list. You can use list1 + list2 to extend list1 with all the elements in list2, so the following would work.
def reducer(arr):
return reduce((lambda result, item: result + item), arr, [])

Related

Python : Create a function that takes a list of integers and strings and returns a new list with the strings filtered out

I am new to coding in Python and I am struggling with a very simple problem. There is the same question but for javascript on the forum but it does not help me.
My code is :
def filter_list(l):
for i in l:
if i != str():
l.append(i)
i = i + 1
return(l)
print(filter_list([1,2,'a','b']))
If you can help!
thanks
Before I present solution here are some problems you need to understand.
str()
str() creates a new instance of the string class. Comparing it to an object with == will only be true if that object is the same string.
print(1 == str())
>>> False
print("some str" == str())
>>> False
print('' == str())
>>> True
iterators (no +1)
You have i = i + 1 in your loop. This doesn't make any sense. i comes from for i in l meaning i looping over the members of list l. There's no guarantee you can add 1 to it. On the next loop i will have a new value
l = [1,2,'a']
for i in l:
print(i)
>>> 1
>>> 2
>>> 'a'
To filter you need a new list
You are appending to l when you find a string. This means that when your loop finds an integer it will append it to the end of the list. And later it will find that integer on another loop interation. And append it to the end AGAIN. And find it in the next iteration.... Forever.
Try it out! See the infinite loop for yourself.
def filter_list(l):
for i in l:
print(i)
if type(i) != str:
l.append(i)
return(l)
filter_list([1,2,'a','b'])
Fix 1: Fix the type check
def filter_list(l):
for i in l:
if type(i) != str:
l.append(i)
return(l)
print(filter_list([1,2,'a','b']))
This infinite loops as discussed above
Fix 2: Create a new output array to push to
def filter_list(l):
output = []
for i in l:
if type(i) != str:
output.append(i)
return output
print(filter_list([1,2,'a','b']))
>>> [1,2]
There we go.
Fix 3: Do it in idiomatic python
Let's use a list comprehension
l = [1,2,'a','b']
output = [x for x in l if type(x) != str]
print(output)
>>> [1, 2]
A list comprehension returns the left most expression x for every element in list l provided the expression on the right (type(x) != str) is true.

return unique python lists of chars ignoring order

Problem:
Consider a python list of lists that contains a sequence of chars:
[['A', 'B'],['A','B','C'],['B','A'],['C','A','B'],['D'],['D'],['Ao','B']]
The goal is to return the unique lists, regardless of order:
[['A','B'],['A','B','C'],['D'],['Ao','B']]
Attempt:
I'm able to achieve my goal using many if/else statements with try/exceptions. What would be the most pythonic (faster) way to approach this problem? Thanks!
def check_duplicates(x,list_):
for li in list_:
if compare(x,li):
return True
def compare(s, t):
t = list(t) # make a mutable copy
try:
for elem in s:
t.remove(elem)
except ValueError:
return False
return not t
vars_list = [['A', 'B'],['A','B','C'],['B','A'],['C','A','B'],['D'],['D'],['Ao','B']]
second_list = []
for i in vars_list:
if check_duplicates(i,second_list):
continue
else:
second_list.append(i)
print(i)
Assuming that the elements of the nested lists are hashable, you can isolate the unique collections by constructing a set of frozensets from the nested list:
unique_sets = {frozenset(l) for l in vars_list}
# {frozenset({'D'}),
# frozenset({'A', 'B'}),
# frozenset({'A', 'B', 'C'}),
# frozenset({'Ao', 'B'})}
If you need a list-of-lists as the output, you can obtain one trivially with [list(s) for s in unique_sets].

How to convert a list with to wildcards into a list of lists using python?

I obtained a list of all files in a folder using glob:
lista = glob.glob("*.h5")
The list basically contains files with names like:
abc_000000000_000.h5
abc_000000000_001.h5
abc_000000000_002.h5
......
abc_000000000_011.h5
......
abc_000000001_000.h5
abc_000000001_001.h5
abc_000000001_002.h5
....
abc_000000026_000.h5
abc_000000026_001.h5
....
abc_000000027_000.h5
....
abc_000000027_011.h5
which has a format abc_0*_0*.h5. How do I reshape this into a list of lists? The inner list would be ['abc_000000027_0*.h5'] and the outer list would be the sequence of the 'abc_000000*' i.e first wildcard.
One way to create an input would be:
lista=[]
for i in range(115):
for j in range(14):
item="abc_%0.9d_%0.3d"%(i,j)
lista.append(item)
My attempt: my solution is not nice and ugly.
listb = glob.glob("*_011.h5")
then for each item in listb split and glob again, for example
listc = glob.glob("abc_000000027*.h5")
Given:
ls -1
abc_00000001_1.h5
abc_00000001_2.h5
abc_00000001_3.h5
abc_00000002_1.h5
abc_00000002_2.h5
abc_00000002_3.h5
abc_00000003_1.h5
abc_00000003_2.h5
abc_00000003_3.h5
You can use pathlib, itertools.groupby and natural sorting to achieve this:
from pathlib import Path
from itertools import groupby
import re
p=Path('/tmp/t2')
def _k(s):
s=str(s)
try:
return tuple(map(int, re.search(r'_(\d+)_(\d*)', s).groups()))
except ValueError:
return (0,0)
def k1(s):
return _k(s)
def k2(s):
return _k(s)[0]
result=[]
files=sorted(p.glob('abc_000000*.h5'), key=k1)
for k,g in groupby(files, key=k2):
result.append(list(map(str, g)))
Which could be simplified to:
def _k(p):
try:
return tuple(map(int, p.stem.split('_')[-2:]))
except ValueError:
return (0,0)
files=sorted(p.glob('abc_000000*_*.h5'), key=lambda e: _k(e))
result=[list(map(str, g)) for k,g in groupby(files, key=lambda e: _k(e)[0])]
Result (in either case):
>>> result
[['/tmp/t2/abc_00000001_1.h5', '/tmp/t2/abc_00000001_2.h5', '/tmp/t2/abc_00000001_3.h5'], ['/tmp/t2/abc_00000002_1.h5', '/tmp/t2/abc_00000002_2.h5', '/tmp/t2/abc_00000002_3.h5'], ['/tmp/t2/abc_00000003_1.h5', '/tmp/t2/abc_00000003_2.h5', '/tmp/t2/abc_00000003_3.h5']]
Which easily could be a dict:
>>> {k:list(map(str, g)) for k,g in groupby(files, key=k2)}
{1: ['/tmp/t2/abc_00000001_1.h5', '/tmp/t2/abc_00000001_2.h5', '/tmp/t2/abc_00000001_3.h5'],
2: ['/tmp/t2/abc_00000002_1.h5', '/tmp/t2/abc_00000002_2.h5', '/tmp/t2/abc_00000002_3.h5'],
3: ['/tmp/t2/abc_00000003_1.h5', '/tmp/t2/abc_00000003_2.h5', '/tmp/t2/abc_00000003_3.h5']}

Deque appending dictionary vs Deque(dictionary) in Python

Try to get my head around adding dictionary to a deque .
Take this example:
from collections import deque
graph={}
graph['key']=['value_1','value_2','value_3']
implement_first=deque()
implement_second=deque(graph['key'])
implement_first.append(graph['key'])
and If I print:
print(implement_first)
print(implement_first.popleft())
I get this deque([['value_1', 'value_2', 'value_3']]) and
['value_1', 'value_2', 'value_3']
and If I print:
print(implement_second)
print(implement_second.popleft())
I get this :
deque(['value_1', 'value_2', 'value_3']) and value_1
So what is going on here ? why am I getting list of list for implement_first.append(graph['key']) and what is this implementation implement_second=deque(graph['key']) does?
The following 2 are not equivalent
d1 = deque(x)
d2 = deque()
d2.append(x)
The deque constructor takes an iterable and appends all its elements. So the following 2 would be the same
d1 = deque(x)
d2 = deque()
for y in x:
d2.append(y)
Yours does not raise any errors because graph["key"] is a list (an iterable) which can be both turned into a deque and be an element of a deque.

Error when searching for the length of a list

[Edit: Solved, i was calling the same function from another place in the code with wrong input arguments, creating this error]
I am new to python and after some search i've decided to post my problem..
My function takes *args as input: a variable number of lists
In this function i use a for loop to read all lists and try to get len(each_list).
I then get the error TypeError: object of type 'numpy.float64' has no len()
I've tried to find the problem by myself but i don't understand the behavior
If i do:
def myfunction(* args):
for p in args:
print(isinstance(p, list))
print(type(p))
print(p)
I'll get: True + class 'list' + [value1, value2, ... etc]
But if i add one line to get the length (last one)
def myfunction(* args):
for p in args:
print(isinstance(p, list))
print(type(p))
print(p)
a = len(p)
I'll get: False + class 'numpy.float64' + error (i understand it is not iterable)
I call the function as:
All_lists = [list1, list2, list3]
myfunction(All_lists)
# I've also tried myfunction(*All_lists)
Thank you for your help
You need to do len(p) for the length of arguments list
def myfunction(* args):
for p in args:
print(isinstance(p, list))
print(type(p))
print(p)
print(len(p))
myfunction([[1,2,3],[4,5,6],[7,8,9] ])
#True
#<class 'list'>
#[1, 2, 3]
#3

Resources