34

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).

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Flavien
  • 7,497
  • 10
  • 45
  • 52

4 Answers4

23

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.

Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
17

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.

michael
  • 827
  • 2
  • 10
  • 11
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.

Julian
  • 3,375
  • 16
  • 27
  • 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. – FrankyBoy Jun 21 '22 at 06:49
2

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
melmi
  • 988
  • 3
  • 9
  • 27