viralogic / py-enumerable

A Python module used for interacting with collections of objects using LINQ syntax
MIT License
188 stars 24 forks source link

Call to any() mutates the collection #72

Closed mzhukovs closed 2 years ago

mzhukovs commented 2 years ago

A call to any() on the Enumerable will mutate it to be out of order after the operation. I suspect the issue affects other, similar methods as well but have not had time to take a closer look.

from py_linq import Enumerable

nums = Enumerable([1,2,3])
nums.any()
print(nums)
# [2,3,1]
viralogic commented 2 years ago

@mzhukovs Yes, order is not guaranteed if you don't use an order_by function as well. This is because Enumerable uses tries to be memory-efficient and use generators to iterate over the underlying collection. If you used something like this, you would have guaranteed order:

nums = Enumerable([1,2,3]).order_by(lambda x: x)
print(nums)

Here is output from my own terminal as an example:

>>> from py_linq import Enumerable
>>> nums = Enumerable([1,2,3]).order_by(lambda x: x)
>>> print(nums)
[1, 2, 3]
>>> print(nums.any())
True
>>> print(nums)
[1, 2, 3]

As you can see, the any function does not mutate the collection ordering. Ordering just isn't guaranteed without using order_by.

mzhukovs commented 2 years ago

This is unintuitive and can lead to some hard to figure out bugs - and in this little test case [1,2,23], the order is simple, but what if its not? One then has to come up with a function to indicate the current order - feels that the default should be the order upon creation should be preserved which matches C# linq behavior (see here UNLESS ordered by another way explicitly (as opposed to having to order_by each time)