-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add index_of, last_index_of, and for_each #71
base: development
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
## for_each | ||
|
||
`for_each(n)` | ||
|
||
Executes a given function per each element in an `Enumerable` collection. Syntactic sugar for a for loop. This is an executing function. | ||
|
||
**Parameters** | ||
|
||
__func__ : the function, lambda or otherwise, to execute per each element | ||
|
||
**Returns** | ||
|
||
None | ||
|
||
**Example** | ||
|
||
<pre><code> | ||
from py_linq import Enumerable | ||
|
||
Enumerable([1 ,2 ,3]).for_each(print) | ||
# 1 | ||
# 2 | ||
# 3 | ||
|
||
Enumerable([ | ||
{'value': 1}, | ||
{'value': 2}, | ||
{'value': 3} | ||
]).for_each(lambda x: print(x['value'])) | ||
# 1 | ||
# 2 | ||
# 3 | ||
|
||
</code></pre> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
## index_of | ||
|
||
`index_of(n)` | ||
|
||
Returns the index of a given element in an `Enumerable` collection. If no matching element is found, None is returned. This is an executing function. | ||
|
||
**Parameters** | ||
|
||
__element__ : the element to search for and return the index of within the collection | ||
|
||
**Returns** | ||
|
||
The index of the given element, or None. | ||
|
||
**Example** | ||
|
||
<pre><code> | ||
from py_linq import Enumerable | ||
|
||
Enumerable([ | ||
{'value': 1}, | ||
{'value': 2}, | ||
{'value': 3} | ||
]).index_of({'value': 2}) | ||
# 1 | ||
|
||
Enumerable([ | ||
{'value': 1}, | ||
{'value': 2}, | ||
{'value': 3} | ||
]).index_of({'value': 4}) | ||
# None | ||
</code></pre> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
## last_index_of | ||
|
||
`last_index_of(n)` | ||
|
||
Returns the index of the last occurrence of a given element in an `Enumerable` collection. If no matching element is found, None is returned. This is an executing function. | ||
|
||
**Parameters** | ||
|
||
__element__ : the element to search for and return the last index of within the collection | ||
|
||
**Returns** | ||
|
||
The last index of the given element, or None. | ||
|
||
**Example** | ||
|
||
<pre><code> | ||
from py_linq import Enumerable | ||
|
||
Enumerable([ | ||
{'value': 1}, | ||
{'value': 2}, | ||
{'value': 3}, | ||
{'value': 1}, | ||
{'value': 2}, | ||
{'value': 3} | ||
]).last_index_of({'value': 2}) | ||
# 4 | ||
|
||
Enumerable([ | ||
{'value': 1}, | ||
{'value': 2}, | ||
{'value': 3} | ||
]).last_index_of({'value': 4}) | ||
# None | ||
</code></pre> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,6 +86,15 @@ def select(self, func=lambda x: x): | |
""" | ||
return SelectEnumerable(Enumerable(iter(self)), func) | ||
|
||
def for_each(self, func=lambda x: x): | ||
""" | ||
Execute a given function per each element | ||
:param func: the function to execute | ||
:return: None | ||
""" | ||
for x in self: | ||
func(x) | ||
|
||
def sum(self, func=lambda x: x): | ||
""" | ||
Returns the sum of af data elements | ||
|
@@ -141,6 +150,33 @@ def median(self, func=lambda x: x): | |
else (float(result[i - 1]) + float(result[i])) / float(2) | ||
) | ||
|
||
def index_of(self, element): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method signature for this should include some Also, what if your collection contains different types of objects (which is possible in an For example:
This above example would fail in the assert because Point(1,2) are two different instances of Point even though they have the same x, y values. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, I guess originally I was just leaving it up to them to implement eq or do a select first then index_of to account for what's needed beforehand, but an optional let's call it normalization function sounds like a good idea. I'll update in a bit. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bit of an impasse on this, issue #72 (not specifically with regards to any() though) makes the behavior of this difficult to predict. Here's what I wanted to do: Updated index_of function: def index_of(self, element, func=lambda x: x, apply_func_to_element=False):
"""
Returns the index of the first occurrence of a given element.
:param element: the element for which to retrieve the index
:param func: optional selector to apply to the collection as lambda expression for equality comparison
:param apply_func_to_element: optional boolean flag indicating whether the selector lambda should be applied to the passed in element as well - default is False
:return: Index of given element
"""
for i, e in enumerate(self):
if func(e) == (func(element) if apply_func_to_element else element):
return i
return None Set up for testing: class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def __eq__(self, other):
if isinstance(other, Rectangle):
return self.length == other.length and self.width == other.width
def area(self):
return self.length * self.width
def perimeter(self):
return 2 * self.length + 2 * self.width
class Square(Rectangle):
def __init__(self, length):
super().__init__(length, length)
self.complex_types = Enumerable([
Rectangle(1, 2),
Rectangle(1, 4),
Rectangle(2, 1),
Rectangle(3, 4),
Square(1),
Square(2),
]) Test: def test_index_of_complex_types(self):
self.assertEqual(4, self.complex_types.index_of(Square(1)))
self.assertEqual(1, self.complex_types.index_of(4, lambda x: x.area()))
self.assertEqual(1, self.complex_types.index_of(Square(2), lambda x: x.area(), True) Those assertions would pass if they were the only ones being executed, but since the Enumerable's order gets changed after a call to any of them, index_of becomes unreliable thereafter. def test_index_of_complex_types(self):
# issue #72 will leave to undesirable behavior without an order_by happening first
complex_types = self.complex_types.order_by(lambda x: (x.length, x.width))
# -> [ Square(1), Rectangle(1,2), Rectangle(1,4), Rectangle(2,1), Square(2), Rectangle(3,4)]
self.assertEqual(2, complex_types.index_of(4, lambda x: x.area()))
# have to do it again unfortunately
complex_types = self.complex_types.order_by(lambda x: (x.length, x.width))
self.assertEqual(2, complex_types.index_of(Square(2), lambda x: x.area(), True)) What are your thoughts on this? |
||
""" | ||
Returns the index of the first occurrence of a given element. | ||
|
||
:param element: the element for which to retrieve the index | ||
:return: Index of given element | ||
""" | ||
for i, e in enumerate(self): | ||
if e == element: | ||
return i | ||
|
||
return None | ||
|
||
def last_index_of(self, element): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comments as for |
||
""" | ||
Returns the index of the last occurrence of a given element. | ||
|
||
:param element: the element for which to retrieve the last index | ||
:return: Index of last occurence of given element | ||
""" | ||
last_index = self.count() - 1 | ||
for i, e in enumerate(self.reverse()): | ||
if e == element: | ||
return last_index - i | ||
|
||
return None | ||
|
||
def element_at(self, n): | ||
""" | ||
Returns element at given index. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this code, it would be an executing function when it probably shouldn't be as there is no need. This would have to be tested, but I think this function could just be a wrapper around the output from the
select
function. For example:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was intentional, basically replicating this where input is Action and return is void: https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.foreach?view=net-6.0