enumerate and zip within Coffeescript? - list-comprehension

Coming from Python, I like many of the features that Coffeescript borrows from Python and Perl (ranges/slices, comprehensions, destructuring assignments). Is there any syntactic sugar in Coffeescript to mimic Python's enumerate or zip (itertools.izip) functions?
Here are the patterns that I don't care much for:
# an enumerate call would be helpful here
i = 0
for x in arr
... use x and i ...
i++
and
# a zip would be useful here
n = Math.min(arr1.length,arr2.length)
for i in 0...n
x = arr1[i]; y = arr2[i]
... use x and y ...

forEach is effectively built in:
a = ['a','b','c']
for el,i in a
alert "Element #{el} is at index #{i}"

Enumerate:
arr.forEach (x, i) ->
... use x and i ...
zip / zipWith (I learned these from Haskell; I assume they mean the same thing in Python?):
zip = (arr1, arr2) ->
basic_zip = (el1, el2) -> [el1, el2]
zipWith basic_zip, arr1, arr2
zipWith = (func, arr1, arr2) ->
min = Math.min arr1.length, arr2.length
ret = []
for i in [0...min]
ret.push func(arr1[i], arr2[i])
ret
Some examples (tested):
zip([1, 2, 3], [4, 5, 6]) # => [[1, 4], [2, 5], [3, 6]]
add = (el1, el2) -> el1 + el2
zipWith(add, [1, 2, 3], [4, 5, 6]) # => [5, 7, 9]
Update: Reimplemented Haskell-style, just for fun. Not as cool without the pattern matching, but oh well..
zipWith = (func, arr1, arr2) ->
return [] if arr1.length is 0 or arr2.length is 0
el1 = arr1.shift()
el2 = arr2.shift()
ret_arr = zipWith func, arr1, arr2
ret_arr.unshift func(el1, el2)
ret_arr
Oh, man, this was fun. SO needs more questions like this :D
Gist for zip and zipWith

For zipping and other such utility functions, Underscore.js is pretty much the standard library—and it happens to have been created by Jeremy Ashkenas, the man behind CoffeeScript. With it, you could write your zip example as
for elems in _.zip(arr1, arr2)
x = elems[0]; y = elems[1]
...
or better yet
for [x, y] in _.zip(arr1, arr2)
...
using pattern-matching. Note, however, that _.zip uses the max length of arr1 and arr2, not the min; so if you don't want to handle undefined values, you should truncate the longer array first.
There's also a CoffeeScript implementation of Underscore, Underscore.coffee, which is a great place to look if you're wondering how to implement a particular loop in CoffeeScript.

The CoffeeScript Cookbook lists a nice implementation of a zip function:
# Usage: zip(arr1, arr2, arr3, ...)
zip = () ->
lengthArray = (arr.length for arr in arguments)
length = Math.min(lengthArray...)
for i in [0...length]
arr[i] for arr in arguments
zip([0, 1, 2, 3], [0, -1, -2, -3])
# => [[0, 0], [1, -1], [2, -2], [3, -3]]
http://coffeescriptcookbook.com/chapters/arrays/zip-function

Don't forget that CoffeeScript is just an alternate syntax for ECMAScript. At least for your first example, there is a perfectly good ECMAscript function (Array.prototype.forEach), which already does what you want:
arr = ["a", "b", "c"]
arr.forEach (el, i) ->
alert "Element #{el} is at index #{i}"
Unfortunately, there is no Array.prototype.zip or Array.prototype.zipWith. That seems to be a pretty big omission, especially considering that there is both reduce and reduceRight, the latter of which many other languages don't have. My guess is that it is a simple oversight, and we are going to see zip in some future version of the language.

For zip, try this one:
zip = (x...) ->
(y[i] for y in x for i in [0...Math.min (y.length for y in x)...])
If you prefer to zip all the way to the end, use Math.max().

Related

Using map python with key argument

I have a function that looks like this:
def myFunct(arg1=None,arg2=None,arg3=None):
pass
I would like to use that function with a map function but with argument 1 and 3 only.
idea would be:
map(myFunct,list_arg1,list_arg3)
so each of the call would be myFunct(value1,arg3=value3)
How could I achieve that ?
You could use lambda to map the arguments to your keyword arguments.
def foo(arg1=None, arg2=None, arg3=None):
return arg1 + arg3
list1 = [3, 4, 5]
list2 = [5, 6, 7]
print(list(map(lambda x, y: foo(arg1=x, arg3=y), list1, list2)))
Another approach is to keep your function as is and modify what you are mapping over:
from itertools import repeat
def f(x = 0, y = 0, z = 0):
return sum((x,y,z))
map(f,range(1,10),repeat(0),range(21,30))
Although from a readability point of view, a simple generator expression might be preferable to any solution based on map, something along the lines of:
f(x = i,z = j) for i,j in zip(range(1,10),range(21,30)))

How can I build new list from the old one in Python adding two new elements at a time?

I work with a tree and my code looks like this
new_lst = [x.left for x in lst if x.left] + [x.right for x in lst if x.right]
Is there an elegant way to rewrite it with just one list comprehension?
Thank you!
You should iterate over the tuple (x.left, x.right) inside the list comprehension.
>>> from collections import namedtuple
>>> Tree = namedtuple('Tree', ['left', 'right'])
>>> L = [Tree(1,2), Tree(0,3), Tree(4,0)]
(for the example, left and right are numbers; they should obviously be subtrees or None).
Your version:
>>> [x.left for x in L if x.left] + [x.right for x in L if x.right]
[1, 4, 2, 3]
In one pass:
>>> [t for x in L for t in (x.left, x.right) if t]
[1, 2, 3, 4]
Note that the order is different.

Python: How to find the average on each array in the list?

Lets say I have a list with three arrays as following:
[(1,2,0),(2,9,6),(2,3,6)]
Is it possible I get the average by diving each "slot" of the arrays in the list.
For example:
(1+2+2)/3, (2+0+9)/3, (0+6+6)/3
and make it become new arraylist with only 3 integers.
You can use zip to associate all of the elements in each of the interior tuples by index
tups = [(1,2,0),(2,9,6),(2,3,6)]
print([sum(x)/len(x) for x in zip(*tups)])
# [1.6666666666666667, 4.666666666666667, 4.0]
You can also do something like sum(x)//len(x) or round(sum(x)/len(x)) inside the list comprehension to get an integer.
Here are couple of ways you can do it.
data = [(1,2,0),(2,9,6),(2,3,6)]
avg_array = []
for tu in data:
avg_array.append(sum(tu)/len(tu))
print(avg_array)
using list comprehension
data = [(1,2,0),(2,9,6),(2,3,6)]
comp = [ sum(i)/len(i) for i in data]
print(comp)
Can be achieved by doing something like this.
Create an empty array. Loop through your current array and use the sum and len functions to calculate averages. Then append the average to your new array.
array = [(1,2,0),(2,9,6),(2,3,6)]
arraynew = []
for i in range(0,len(array)):
arraynew.append(sum(array[i]) / len(array[i]))
print arraynew
As you were told in the comments with sum and len it's pretty easy.
But in python I would do something like this, assuming you want to maintain decimal precision:
list = [(1, 2, 0), (2, 9, 6), (2, 3, 6)]
res = map(lambda l: round(float(sum(l)) / len(l), 2), list)
Output:
[1.0, 5.67, 3.67]
But as you said you wanted 3 ints in your question, would be like this:
res = map(lambda l: sum(l) / len(l), list)
Output:
[1, 5, 3]
Edit:
To sum the same index of each tuple, the most elegant method is the solution provided by #PatrickHaugh.
On the other hand, if you are not fond of list comprehensions and some built in functions as zip is, here's a little longer and less elegant version using a for loop:
arr = []
for i in range(0, len(list)):
arr.append(sum(l[i] for l in list) / len(list))
print(arr)
Output:
[1, 4, 4]

How to assing values to a dictionary

I am creating a function which is supposed to return a dictionary with keys and values from different lists. But I amhavin problems in getting the mean of a list o numbers as values of the dictionary. However, I think I am getting the keys properly.
This is what I get so far:
def exp (magnitudes,measures):
"""return for each magnitude the associated mean of numbers from a list"""
dict_expe = {}
for mag in magnitudes:
dict_expe[mag] = 0
for mea in measures:
summ = 0
for n in mea:
summ += n
dict_expe[mag] = summ/len(mea)
return dict_expe
print(exp(['mag1', 'mag2', 'mag3'], [[1,2,3],[3,4],[5]]))
The output should be:
{mag1 : 2, mag2: 3.5, mag3: 5}
But what I am getting is always 5 as values of all keys. I thought about the zip() method but im trying to avoid it as because the it requieres the same length in both lists.
An average of a sequence is sum(sequence) / len(sequence), so you need to iterate through both magnitudes and measures, calculate these means (arithmetical averages) and store it in a dictionary.
There are much more pythonic ways you can achieve this. All of these examples produce {'mag1': 2.0, 'mag2': 3.5, 'mag3': 5.0} as result.
Using for i in range() loop:
def exp(magnitudes, measures):
means = {}
for i in range(len(magnitudes)):
means[magnitudes[i]] = sum(measures[i]) / len(measures[i])
return means
print(exp(['mag1', 'mag2', 'mag3'], [[1, 2, 3], [3, 4], [5]]))
But if you need both indices and values of a list you can use for i, val in enumerate(sequence) approach which is much more suitable in this case:
def exp(magnitudes, measures):
means = {}
for i, mag in enumerate(magnitudes):
means[mag] = sum(measures[i]) / len(measures[i])
return means
print(exp(['mag1', 'mag2', 'mag3'], [[1, 2, 3], [3, 4], [5]]))
Another problem hides here: i index belongs to magnitudes but we are also getting values from measures using it, this is not a big deal in your case if you have magnitudes and measures the same length but if magnitudes will be larger you will get an IndexError. So it seems to me like using zip function is what would be the best choice here (actually as of python3.6 it doesn't require two lists to be the same length, it will just use the length of shortest one as the length of result):
def exp(magnitudes, measures):
means = {}
for mag, mes in zip(magnitudes, measures):
means[mag] = sum(mes) / len(mes)
return means
print(exp(['mag1', 'mag2', 'mag3'], [[1, 2, 3], [3, 4], [5]]))
So feel free to use the example which suits your requirements of which one you like and don't forget to add docstring.
More likely you don't need such pythonic way but it can be even shorter when dictionary comprehension comes into play:
def exp(magnitudes, measures):
return {mag: sum(mes) / len(mes) for mag, mes in zip(magnitudes, measures)}
print(exp(['mag1', 'mag2', 'mag3'], [[1, 2, 3], [3, 4], [5]]))

How to pair two list in python

I think someone may have asked this question already, but for some reasons I just cannot come out good key words to find the answers for it.
I have two separate lists, and I could like to pair them.
list_a = [[1,2] [3,4]]
list_b = [[5],[6]]
I would like to generate:
list_c = [[[1,2],[5]],[[3,4],[6]]]
Thank you for your help
The following code should do the trick!
list_c = [[x, y] for x, y in zip(list_a, list_b)]
The zip function acts to 'pair' the list elements together, while the list comprehension builds the new list.
If you want to append them to a new list, this is what you want:
list_a = [[1,2], [3,4]]
list_b = [[5],[6]]
list_res = []
for a, b in zip(list_a, list_b):
list_res.append([a, b])
>list_res
>[[[1, 2], [5]], [[3, 4], [6]]]

Resources