Python equivalents to LINQ
Asked Answered
P

4

37

In C#, with LINQ, if I have en enumeration enumerable, I can do:

// a: Does the enumerable contain an item that satisfies the lambda?
bool contains = enumerable.Any(lambda);

// b: How many items satisfy the lambda?
int count = enumerable.Count(lambda);

// c: Return an enumerable that contains only distinct elements according to my custom comparer
var distinct = enumerable.Distinct(comparer);

// d: Return the first element that satisfies the lambda, or throws an exception if none
var element = enumerable.First(lambda);

// e: Returns an enumerable containing all the elements except those
// that are also in 'other', equality being defined by my comparer
var except = enumerable.Except(other, comparer);

I hear that Python has a more concise syntax than C# (and is therefore more productive), so how do I achieve the same with an iterable in Python, with the same amount of code, or less?

Note: I don't want to materialize the iterable into a list if I don't have to (Any, Count, First).

Proselytism answered 20/8, 2012 at 17:15 Comment(0)
W
27

The following Python lines should be equivalent to what you have (assuming func, or lambda in your code, returns a Boolean):

# Any
contains = any(func(x) for x in enumerable)

# Count
count = sum(func(x) for x in enumerable)

# Distinct: since we are using a custom comparer here, we need a loop to keep 
# track of what has been seen already
distinct = []
seen = set()
for x in enumerable:
    comp = comparer(x)
    if not comp in seen:
        seen.add(comp)
        distinct.append(x)

# First
element = next(iter(enumerable))

# Except
except_ = [x for x in enumerable if not comparer(x) in other]

References:

Note that I renamed lambda to func since lambda is a keyword in Python, and I renamed except to except_ for the same reason.

Note that you could also use map() instead of the comprehensions/generators, but it is generally considered less readable.

Wigwam answered 20/8, 2012 at 17:19 Comment(5)
Thanks, you missed d: enumerable.First(lambda), can you add it?Proselytism
@Proselytism - Added it, I also modified the enumerable.Distinct equivalent. My original version was giving a set of the comparer results, instead of using the values from the original enumerable.Wigwam
Without the custom comparer, you can just use distinct = set(enumerable)Bittersweet
What about performance? Is it good as .Net equivalent?Con
why sum() and not len()?Cush
M
24

The original question was how to achieve the same functionality with iterables in Python. As much as I enjoy list comprehensions, I still find LINQ more readable, intuitive and concise in many situations. The following libraries wrap Python iterables to achieve the same functionality in Python with the same LINQ semantics:

If you want to stick with built in Python functionality, this blog post provides a fairly thorough mapping of C# LINQ functionality to built-in Python commands.

Midian answered 17/1, 2020 at 22:13 Comment(0)
T
9

We have generator expressions and various functions for expressing arbitrary conditions over iterables.

any(some_function(e) for e in iterable)
sum(1 for e in iterable if some_function(e))
set(iterable)
next(iterable)
(e for e in iterable if not comparer(e) in other)

would roughly correspond to how you write your examples in idiomatic Python.

Torsibility answered 20/8, 2012 at 17:20 Comment(1)
while that's cute and all it becomes a horrible mess once you want to chain multiple actions together, which is super trivial in linq.Accent
I
3

Although this question is five years old I don't know why nobody mentioned the beautiful pipe library. It has a very beautiful interface and is also very extensible.

I usually add these functions to it:

@Pipe
def collect(iter, f):
    return f(iter)

@Pipe
def reduce(iterable, function, initial=None):
    return functools.reduce(function, iterable, initial)

_sum = reduce(lambda x, y: x + y, 0)
_len = reduce(lambda x, _: x + 1, 0)

so I can have something like

my_numbers = (
    count(1)
    | where(lambda n: n%2 == 0)
    | take_while(lambda n: n < 100)
    | collect(list)
)

my_numbers_len = my_numbers | _len
my_numbers_sum = my_numbers | _sum
Invulnerable answered 2/8, 2023 at 21:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.