How to unpack tuple in arguments with sugar in Nim? - nim-lang

Is it possible to unpack tuple in arguments?
import sugar, sequtils
echo #[(1, 1), (2, 2)].filter((point) => point[0] > 1) # Works
echo #[(1, 1), (2, 2)].filter(((x, y)) => x > 1) # Error

The sugar macro => can sort of unpack tuple arguments, as the following example (taken from docs) shows:
proc passTwoAndTwo(f: (int, int) -> bool): bool =
f(2, 2)
echo passTwoAndTwo((x, y) => x > 1) # true
Sort of, since it is actually treating x and y as two different arguments, not as element of a tuple.
In fact, the problem is the syntax ((x, y)) => x > 1 which is not supported by => (you can check that by running it with a discard in front).
I am not sure how easy or reasonable would be to require => to support the special case of passing a tuple.
For your use cases the following two lines are working alternatives once you have a Point type defined:
echo #[(1, 1).Point, (2, 2)].filter(point => point.x > 1)
echo #[(1, 1), (2, 2)].filterIt(it.Point.x > 1)

Related

Keep duplciate items in list of tuples if only the first index matches between the tuples

Input [(1,3), (3,1), (1,5), (2,3), (2,4), (44,33), (33,22), (44,22), (22,33)]
Expected Output [(1,3), (1,5), (2,3), (2,4), (44,33), (44,22)]
I am trying to figure out the above and have tried lots of stuff. So far my only success has been,
for x in range(len(list1)):
if list1[0][0] == list1[x][0]:
print(list1[x])
Output: (1, 3) \n (1, 5)
Any sort of advice or help would be appreciated.
Use a collections.defaultdict(list) keyed by the first value, and keep only the values that are ultimately duplicated:
from collections import defaultdict # At top of file, for collecting values by first element
from itertools import chain # At top of file, for flattening result
dct = defaultdict(list)
inp = [(1,3), (3,1), (1,5), (2,3), (2,4), (44,33), (33,22), (44,22), (22,33)]
# For each tuple
for tup in inp:
first, _ = tup # Extract first element (and verify it's actually a pair)
dct[first].append(tup) # Collect with other tuples sharing the same first element
# Extract all lists which have two or more elements (first element duplicated at least once)
# Would be list of lists, with each inner list sharing the same first element
onlydups = [lst for firstelem, lst in dct.items() if len(lst) > 1]
# Flattens to make single list of all results (if desired)
flattened_output = list(chain.from_iterable(onlydups))
Importantly, this doesn't required ordered input, and scales well, doing O(n) work (scaling your solution naively would produce a O(n²) solution, considerably slower for larger inputs).
Another approach is the following :
def sort(L:list):
K = []
for i in L :
if set(i) not in K :
K.append(set(i))
output = [tuple(m) for m in K]
return output
output :
[(1, 3), (1, 5), (2, 3), (2, 4), (33, 44), (33, 22), (44, 22)]

SymPy result Filtering

I was recently working on a CodeForce problem
So, I was using SymPy to solve this.
My code is :
from sympy import *
x,y = symbols("x,y", integer = True)
m,n = input().split(" ")
sol = solve([x**2 + y - int(n), y**2 + x - int(m)], [x, y])
print(sol)
What I wanted to do:
Filter only Positive and integer value from SymPy
Ex: If I put 14 28 in the terminal it will give me tons of result, but I just want it to show [(5, 3)]
I don't think that this is the intended way to solve the code force problem (I think you're just supposed to loop over the possible values for one of the variables).
I'll show how to make use of SymPy here anyway though. Your problem is a diophantine system of equations. Although SymPy has a diophantine solver it only works for individual equations rather than systems.
Usually the idea of using a CAS for something like this though is to symbolically find something like a general result that then helps you to write faster concrete numerical code. Here are your equations with m and n as arbitrary symbols:
In [62]: x, y, m, n = symbols('x, y, m, n')
In [63]: eqs = [x**2 + y - n, y**2 + x - m]
Using the polynomial resultant we can eliminate either x or y from this system to obtain a quartic polynomial for the remaining variable:
In [31]: py = resultant(eqs[0], eqs[1], x)
In [32]: py
Out[32]:
2 2 4
m - 2⋅m⋅y - n + y + y
While there is a quartic general formula that SymPy can use (if you use solve or roots here) it is too complicated to be useful for a problem like the one that you are describing. Instead though the rational root theorem tells us that an integer root for y must be a divisor of the constant term:
In [33]: py.coeff(y, 0)
Out[33]:
2
m - n
Therefore the possible values for y are:
In [64]: yvals = divisors(py.coeff(y, 0).subs({m:14, n:28}))
In [65]: yvals
Out[65]: [1, 2, 3, 4, 6, 7, 8, 12, 14, 21, 24, 28, 42, 56, 84, 168]
Since x is m - y**2 the corresponding values for x are:
In [66]: solve(eqs[1], x)
Out[66]:
⎡ 2⎤
⎣m - y ⎦
In [67]: xvals = [14 - yv**2 for yv in yvals]
In [68]: xvals
Out[68]: [13, 10, 5, -2, -22, -35, -50, -130, -182, -427, -562, -770, -1750, -3122, -7042, -28210]
The candidate solutions are then given by:
In [69]: candidates = [(xv, yv) for xv, yv in zip(xvals, yvals) if xv > 0]
In [70]: candidates
Out[70]: [(13, 1), (10, 2), (5, 3)]
From there you can test which values are solutions:
In [74]: eqsmn = [eq.subs({m:14, n:28}) for eq in eqs]
In [75]: [c for c in candidates if all(eq.subs(zip([x,y],c))==0 for eq in eqsmn)]
Out[75]: [(5, 3)]
The algorithmically minded will probably see from the above example how to make a much more efficient way of implementing the solver.
I've figured out the answer to my question ! At first, I was trying to filter the result from solve(). But there is an easy way to do this.
Pseudo code:
solve() gives the intersection point of both Parabolic Equations as a List
I just need to filter() the other types of values. Which in my case is <sympy.core.add.Add>
def rem(_list):
return list(filter(lambda v: type(v) != Add, _list))
Yes, You can also use type(v) == int
Final code:
from sympy import *
# the other values were <sympy.core.add.Add> type. So, I just defined a function to filterOUT these specific types from my list.
def rem(_list):
return list(filter(lambda v: type(v) != Add, _list))
x,y = symbols("x,y", integer = True, negative = False)
output = []
m,n = input().split(' ')
# I need to solve these 2 equations separately. Otherwise, my defined function will not work without loop.
solX = rem(solve((x+(int(n)-x**2)**2 - int(m)), x))
solY = rem(solve((int(m) - y**2)**2 + y - int(n), y))
if len(solX) == 0 or len(solY) == 0:
print(0)
else:
output.extend(solX) # using "Extend" to add multiple values in the list.
output.extend(solY)
print(int((len(output))/2)) # Obviously, result will come in pairs. So, I need to divide the length of the list by 2.
Why I used this way :
I tried to solve it by algorithmic way, but it still had some float numbers. I just wanted to skip the loop thing here again !
As sympy solve() has already found the values. So, I skipped the other way and focused on filtering !
Sadly, code force compiler shows a runtime error! I guess it can't import sympy. However, it works fine in VSCode.

creating a list of tuples based on successive items of initial list [duplicate]

I sometimes need to iterate a list in Python looking at the "current" element and the "next" element. I have, till now, done so with code like:
for current, next in zip(the_list, the_list[1:]):
# Do something
This works and does what I expect, but is there's a more idiomatic or efficient way to do the same thing?
Some answers to this problem can simplify by addressing the specific case of taking only two elements at a time. For the general case of N elements at a time, see Rolling or sliding window iterator?.
The documentation for 3.8 provides this recipe:
import itertools
def pairwise(iterable):
"s -> (s0, s1), (s1, s2), (s2, s3), ..."
a, b = itertools.tee(iterable)
next(b, None)
return zip(a, b)
For Python 2, use itertools.izip instead of zip to get the same kind of lazy iterator (zip will instead create a list):
import itertools
def pairwise(iterable):
"s -> (s0, s1), (s1, s2), (s2, s3), ..."
a, b = itertools.tee(iterable)
next(b, None)
return itertools.izip(a, b)
How this works:
First, two parallel iterators, a and b are created (the tee() call), both pointing to the first element of the original iterable. The second iterator, b is moved 1 step forward (the next(b, None)) call). At this point a points to s0 and b points to s1. Both a and b can traverse the original iterator independently - the izip function takes the two iterators and makes pairs of the returned elements, advancing both iterators at the same pace.
Since tee() can take an n parameter (the number of iterators to produce), the same technique can be adapted to produce a larger "window". For example:
def threes(iterator):
"s -> (s0, s1, s2), (s1, s2, s3), (s2, s3, 4), ..."
a, b, c = itertools.tee(iterator, 3)
next(b, None)
next(c, None)
next(c, None)
return zip(a, b, c)
Caveat: If one of the iterators produced by tee advances further than the others, then the implementation needs to keep the consumed elements in memory until every iterator has consumed them (it cannot 'rewind' the original iterator). Here it doesn't matter because one iterator is only 1 step ahead of the other, but in general it's easy to use a lot of memory this way.
Roll your own!
def pairwise(iterable):
it = iter(iterable)
a = next(it, None)
for b in it:
yield (a, b)
a = b
Starting in Python 3.10, this is the exact role of the pairwise function:
from itertools import pairwise
list(pairwise([1, 2, 3, 4, 5]))
# [(1, 2), (2, 3), (3, 4), (4, 5)]
or simply pairwise([1, 2, 3, 4, 5]) if you don't need the result as a list.
I’m just putting this out, I’m very surprised no one has thought of enumerate().
for (index, thing) in enumerate(the_list):
if index < len(the_list):
current, next_ = thing, the_list[index + 1]
#do something
Since the_list[1:] actually creates a copy of the whole list (excluding its first element), and zip() creates a list of tuples immediately when called, in total three copies of your list are created. If your list is very large, you might prefer
from itertools import izip, islice
for current_item, next_item in izip(the_list, islice(the_list, 1, None)):
print(current_item, next_item)
which does not copy the list at all.
Iterating by index can do the same thing:
#!/usr/bin/python
the_list = [1, 2, 3, 4]
for i in xrange(len(the_list) - 1):
current_item, next_item = the_list[i], the_list[i + 1]
print(current_item, next_item)
Output:
(1, 2)
(2, 3)
(3, 4)
I am really surprised nobody has mentioned the shorter, simpler and most importantly general solution:
Python 3:
from itertools import islice
def n_wise(iterable, n):
return zip(*(islice(iterable, i, None) for i in range(n)))
Python 2:
from itertools import izip, islice
def n_wise(iterable, n):
return izip(*(islice(iterable, i, None) for i in xrange(n)))
It works for pairwise iteration by passing n=2, but can handle any higher number:
>>> for a, b in n_wise('Hello!', 2):
>>> print(a, b)
H e
e l
l l
l o
o !
>>> for a, b, c, d in n_wise('Hello World!', 4):
>>> print(a, b, c, d)
H e l l
e l l o
l l o
l o W
o W o
W o r
W o r l
o r l d
r l d !
This is now a simple Import As of 16th May 2020
from more_itertools import pairwise
for current, next in pairwise(your_iterable):
print(f'Current = {current}, next = {nxt}')
Docs for more-itertools
Under the hood this code is the same as that in the other answers, but I much prefer imports when available.
If you don't already have it installed then:
pip install more-itertools
Example
For instance if you had the fibbonnacci sequence, you could calculate the ratios of subsequent pairs as:
from more_itertools import pairwise
fib= [1,1,2,3,5,8,13]
for current, nxt in pairwise(fib):
ratio=current/nxt
print(f'Curent = {current}, next = {nxt}, ratio = {ratio} ')
As others have pointed out, itertools.pairwise() is the way to go on recent versions of Python. However, for 3.8+, a fun and somewhat more concise (compared to the other solutions that have been posted) option that does not require an extra import comes via the walrus operator:
def pairwise(iterable):
a = next(iterable)
yield from ((a, a := b) for b in iterable)
A basic solution:
def neighbors( list ):
i = 0
while i + 1 < len( list ):
yield ( list[ i ], list[ i + 1 ] )
i += 1
for ( x, y ) in neighbors( list ):
print( x, y )
Pairs from a list using a list comprehension
the_list = [1, 2, 3, 4]
pairs = [[the_list[i], the_list[i + 1]] for i in range(len(the_list) - 1)]
for [current_item, next_item] in pairs:
print(current_item, next_item)
Output:
(1, 2)
(2, 3)
(3, 4)
code = '0016364ee0942aa7cc04a8189ef3'
# Getting the current and next item
print [code[idx]+code[idx+1] for idx in range(len(code)-1)]
# Getting the pair
print [code[idx*2]+code[idx*2+1] for idx in range(len(code)/2)]

Strange behavior on lambda and Dict.items()

I'm trying to replace duplicate words from string with the code bellow:
from functools import reduce
from collections import Counter
import re
if __name__ == '__main__':
sentence = 'User Key Account Department Account Start Date'
result = reduce(
lambda sentence, word: re.sub(rf'{word}\s*', '', sentence, count=1),
filter(lambda x: x[0] if x[1] > 1 else '',
Counter(sentence.split()).items()),
sentence
)
import pdb
pdb.set_trace()
print(result)
# User Key Department Account Start Date
But it does not print the expected. The strange part is in filter. If I list only results filtered:
[el for el in filter(lambda x: x[0] if x[1] > 1 else '', Counter(sentence.split()).items())]
# [('Account', 2)]
Despite what is specified in lambda, x[0].
If I pass a not false value to else clause:
[el for el in filter(lambda x: x[0] if x[1] > 1 else ['foo'], Counter(sentence.split()).items())]
# [('User', 1), ('Key', 1), ('Account', 2), ('Department', 1), ('Start', 1), ('Date', 1)]
What I'm missing here?
I'd like to do the following:
[el for el in filter(lambda key,value: key if value > 1 else '', Counter(sentence.split()).items())]
And get Account. But it raises *** TypeError: <lambda>() missing 1 required positional argument: 'value'
It works fine using list comprehension:
[key for key, value in Counter(sentence.split()).items() if value > 1]
# ['Account']
The issue here is that I'm not sure what you're trying to do here. But I will explain what's actually happening.
Consider the expression filter(lambda x: x[0] if x[1] > 1 else '', Counter(sentence.split()).items()).
The first argument to filter is a predicate. This is a function which takes one input (x) and returns a value which is interpreted as a Boolean.
In this case, let's consider the predicate lambda x : x[0] if x[1] > 1 else '' - we will write this as P for shorthand. We will assume we call this function on an ordered pair (a, b) such that a is a string and b is a number.
Then we see that P((a, b)) = a if b > 1 else ''.
So if b > 1, then P((a, b)) evaluates to a. This value is then interpreted as a Boolean (even though it's a string) because P serves as a predicate.
When we interpret some "container" data-type like a String as a Boolean, we interpret the container to be "true-like" if it is non-empty and "false-like" if it is empty. So in this case, a will be interpreted as True when a is non-empty and False when a is empty.
On the other hand, when b <= 1, P((a, b)) will evaluate to '' which is then interpreted as False (because it's the empty string).
So P((a, b)) is a string which, when interpreted as a Boolean, is equal to b > 1 and (a is non-empty).
So when we call filter(P, seq), where seq is a sequence of pairs (a, b), a a string and b a number, we see that we will keep exactly those pairs (a, b) where b > 1 and a is non-empty.
This is indeed what happens.
However, it seems that what you want to happen is to only keep the items which occur more than once while ignoring their count. To do this, you need a combination of map and filter. You would want
map(lambda x: x[0], filter(lambda x: x[1] > 1, Counter(sentence.split()).items()))
This first keeps only the pairs (a, b) where b > 1. It then takes each remaining pair (a, b) and keeps only the a.

How to use python assignment operator with multiple return values

I have a Python function which takes a parameter and returns 2 outputs. I want to call this for all the elements in a list and combine the return values for each invocation as below:
a1, b1 = func(p[0])
a2, b2 = func(p[1])
a3, b3 = func(p[3])
a = a1 & a2 & a3
b = b1 + b2 + b3
How can I refactor this to a one liner using the += , &= and the reduce operators in Python?
Thanks
Since you want a one-liner, something like this should help you:
a,b = int(all(ele == list(zip(*map(func,p)))[0][0] for ele in list(zip(*map(func,p)))[0])),sum(list(zip(*map(func,p)))[-1])
Breakdown:
The map function applies a function to an iterable.
list(map(func,p))
Prints:
[(1, 3), (2, 4), (3, 5)]
This:
list(zip([(1, 3), (2, 4), (3, 5)]))
Prints:
[((1, 3),), ((2, 4),), ((3, 5),)]
Adding an * before map would transpose the list:
list(zip(*map(func,p)))
Prints:
[(1, 2, 3), (3, 4, 5)]
The all function is combined with a simple list comprehension in order to find if all elements of sublist 1 are equal. Here is a simpler version of it:
all(ele == lst[0] for ele in lst)
The same has been applied in this case like this:
all(ele == list(zip(*map(func,p)))[0][0] for ele in list(zip(*map(func,p)))[0])
Prints:
False
In order to convert True/False to 1/0, I have used the int() function:
int(all(ele == list(zip(*map(func,p)))[0][0] for ele in list(zip(*map(func,p)))[0]))
Prints:
0
The second part of it, which is this: sum(list(zip(*map(func,p)))[-1]) calculates the sum of the second sublist.
sum(list(zip(*map(func,p)))[-1])
Prints:
12
Here is the full code:
def func(a):
return a,a+2
p = [1,2,3]
a,b = all(ele == list(zip(*map(func,p)))[0][0] for ele in list(zip(*map(func,p)))[0])),sum(list(zip(*map(func,p)))[-1])
print(a,b)
Output:
(0, 12)
We can decompose this problem into individual tasks:
Mapping a function func across the elements p[0], p[1], ... of an iterable/sequence p is map(func, p).
This already provides an iterable of (a1, b1), (a2, b2) so the assignment does not need special handling.
The chain of operations a1 & a2 & ... for a sequence/iterable a is reduce(lambda new, total: new & total) and likewise for +.
Since we have a stream of pairs of (a1, b1), ... instead of a pair of streams a1, ... and b1, ... we must operate on both at once.
The result of reducing a stream of pairs is a single pair, which we can assign to two variables as a, b = result.
from functools import reduce
def func(a): return a, a
p = range(1, 5, 2)
def reduction(new, total):
return new[0] & total[0], new[1] + total[1]
mapped = map(func, p)
reduced = reduce(reduction, mapped)
a, b = reduced
If desired, this can be written in one statement/line.
>>> a, b = reduce(lambda new, total: (new[0] & total[0], new[1] + total[1]), map(func, p))
>>> a
1
>>> b
4

Resources