Why is Map function not computing my lambda? - python

So I'm trying to use the map function with a lambda to write each item of a list to a txt file on a new line
map(lambda x: text_file.write(f"{x}\n"), itemlist_with_counts_formatted)
I understand that map returns a map object, but I don't need the return value.
What I want is for the map function to compute the lambda, which adds "\n" to the end of each item in the given list.
I thought that map should do this (compute the function (lambda appends "\n") using arguments from the iterable) but nothing gets output to the txt file.
For clarity, I can totally do this with a list comprehension but I wanted to learn how to use map (and properly anonymous lambdas), so am looking for help solving it using these two functions specifically (if possible).
map(lambda x: text_file.write(f"{x}\n"), itemlist_with_counts_formatted)
I have also tried it without the f string, using just x + "\n" but this doesn't work either
Yes the txt file is open, and yes I can get it to work using other methods, the problem is exclusive to how I'm using map or how I'm using lambda, which must be wrong in some way. I've been doing this for 6 weeks so its probably something stupid but I've tried to figure it out myself and i just can't and I've checked but can't find anything on here - appreciate any help I can get.

You should really not use map for this task.
It looks fancy, but this is the same as using list comprehensions for side effects. It's considered bad practice.
[print(i) for i in range(3)]
Which should be replaced with:
for i in range(3):
print(i)
In you case, use:
for item in itemlist_with_counts_formatted:
text_file.write(f"item\n")
why your code did not work:
map returns a generator, nothing is evaluated until something consumes the generator. You would need to do:
list(map(lambda x: text_file.write(f"{x}\n"), itemlist_with_counts_formatted))
But, again, don't, this is useless, less efficient and less explicit.
But I really want a one-liner!
Then use:
text_file.write('\n'.join(itemlist_with_counts_formatted))
NB. unlike the other alternatives in this answer, this one does not add a trailing '\n' in the end of the file.
I really, really, want to use map:
text_file.writelines(map(lambda x: f'{x}\n', itemlist_with_counts_formatted))

i think that the problem is that this use of the map function is a bit unproper. As said in the documentation the map function returns a generator for the results iterable, while the write function is not returning anything. This might brake something during the map internals.
I'd suggest you to use map only to add line end and then use the writeline function on the resulting generator, something like:
text_file.writelines(map(lambda x: f"{x}\n", itemlist_with_counts_formatted))
(Not tested)

Related

How can I pass each element of a set to a function?

I have a set with multiple tuples: set1 = {(1,1),(2,1)} for example.
Now I want to pass each tuple of the set to a method with this signature: process_tuple(self, tuple).
I am doing it with a for loop like this:
for tuple in set1:
process_tuple(tuple)
Is there a better way to do it?
Your question is basically "how can I loop without using a loop". While it's possible to do what you're asking with out an explicit for loop, the loop is by far the clearest and best way to go.
There are some alternatives, but mostly they're just changing how the loop looks, not preventing it in the first place. If you want to collect the return values from the calls to your function in a list, you can use a list comprehension to build the list at the same time as you loop:
results = [process_tuple(tuple) for tuple in set1]
You can also do set or dict comprehensions if those seem useful to your specific needs. For example, you could build a dictionary mapping from the tuples in your set to their processed results with:
results_dict = {tuple: process_tuple(tuple) for tuple in set1}
If you don't want to write out for tuple in set1 at all, you could use the builtin map function to do the looping and passing of values for you. It returns an iterator, which you'll need to fully consume to run the function over the full input. Passing the map object to list sometimes makes sense, for instance, to convert inputs into numbers:
user_numbers = list(map(int, input("Enter space-separated integers: ").split()))
But I'd also strongly encourage you to think of your current code as perhaps the best solution. Just because you can change it to something else, doesn't mean you should.

Python removing multiple files using map and os.remove

I am trying to remove multiple files using this method:
map(os.remove, glob.glob("*.pdf"))
I am getting a list of files with pdf extension but this does not remove any files.
My solution was to wrap this map with list().
Any other solution that does not require using list or something?
Use a for loop:
for i in glob.glob('*.pdf'):
os.remove(i)
The reason why map(...) doesn't work by itself is that map(...) will return a generator and it will only evaluate the expressions once the items are actually accessed from the generator.
Furthermore, the point of map(...) is to group the results of the expressions returned by the function that is called on every item, but that doesn't really make sense here since os.remove(...) doesn't really return anything (or, in other words, returns None), so since assembling a list of Nones doesn't really serve much purpose (you're throwing it out right away anyways), using a for loop is a more appropriate way to approach this task.

miniscule annoyance with list comprehension and iterators

Is there a way to write the following expression in way
that I will not repeat the variables twice? it's very annoying
when you have to write it multiple times.
['hello {} {}'.format(A,B) for A,B in product(As,Bs)]
That's exactly what you would do for a for loop anyway isn't it? Anyway, you can use map:
map(lambda x:'hello {} {}'.format(*x), product(As,Bs)]
I realize now the parameter expansion *x syntax might shorten what you wrote above as well (one variable only). If you intend to apply someFormatString.format(*x) always you can shorten this using a function to generate your formatter:
def mkStr(formatStr):
return lambda x,f=formatStr: f.format(*x)
Then you can map with
map(mkStr('hello {} {}'), product(As,Bs)]
and switch it up with different format strings.
with only a minor change you could get there:
['hello {} {}'.format(*ab) for ab in product(As, Bs)]
using *args-magic.

cleanest way to call one function on a list of items

In python 2, I used map to apply a function to several items, for instance, to remove all items matching a pattern:
map(os.remove,glob.glob("*.pyc"))
Of course I ignore the return code of os.remove, I just want all files to be deleted. It created a temp instance of a list for nothing, but it worked.
With Python 3, as map returns an iterator and not a list, the above code does nothing.
I found a workaround, since os.remove returns None, I use any to force iteration on the full list, without creating a list (better performance)
any(map(os.remove,glob.glob("*.pyc")))
But it seems a bit hazardous, specially when applying it to methods that return something. Another way to do that with a one-liner and not create an unnecessary list?
The change from map() (and many other functions from 2.7 to 3.x) returning a generator instead of a list is a memory saving technique. For most cases, there is no performance penalty to writing out the loop more formally (it may even be preferred for readability).
I would provide an example, but #vaultah nailed it in the comments: still a one-liner:
for x in glob.glob("*.pyc"): os.remove(x)

Convert numpy.ndarray to list (Python)

I am running a function developed by Esri to get list of values in a integer column of a spatial table (however, the same behaviour is observed even when running the function on a non-spatial table). According to the help, I should get NumPy structured array. After running the function, I have a numpy array. I run print in this format:
in_table = r"C:\geodb101#server.sde\DataTable" #
data = arcpy.da.TableToNumPyArray(in_table, "Field3")
print data
Which gives me back this in IDE (copy/pasted from IDE interpreter):
[(20130825,) (20130827,) (20130102,)]
I am running:
allvalues = data.tolist()
and getting:
[(20130825,), (20130827,), (20130102,)]
Same result when running data.reshape(len(data)).tolist() as suggested in comments.
Running type() lets me know that in the first case it is <type 'numpy.ndarray'> and in the second case <type 'list'>. I am expecting to get my output list in another format [20130825, 20130827, 20130102]. What am I doing wrong or what else should I do to get the output list in the specified format?
I have a possible approach, but I'm not 100% sure it will work, as I can't figure out how you got tuples into an array (when I tried to create an array of tuples, it looks like the tuples got converted to arrays). In any case, give this a shot:
my_list = map(lambda x: x[0], my_np_array_with_tuples_in_it)
This assumes you're dealing specifically with the single element tuples you describe above. And like I said, when I tried to recreate your circumstances, numpy did some conversion moves that I don't fully understand (not really a numpy expert).
Hope that helps.
Update: Just saw the new edits. Not sure if my answer applies anymore.
Update 2: Glad that worked, here's a bit of elaboration.
Lambda is basically just an inline function, and is a construct common in a lot of languages. It's essentially a temporary, anonymous function. You could have just as easily done something like this:
def my_main_func():
def extract_tuple_value(tup):
return tup[0]
my_list = map(extract_tuple_value, my_np_array_with_tuples_in_it)
But as you can see, the lambda version is more concise. The "x" in my initial example is the equivalent of "tup" in the more verbose example.
Lambda expressions are generally limited to very simple operations, basically one line of logic, which is what is returned (there is no explicit return statement).
Update 3: After chatting with a buddy and doing some research, list comprehension is definitely the way to go (see Python List Comprehension Vs. Map).
From acushner's comment below, you can definitely go with this instead:
my_list = [tup[0] for tup in my_np_array_with_tuples_in_it]

Categories

Resources