From d1c5e88acc12445c9d8440dc204dab9af48c842a Mon Sep 17 00:00:00 2001 From: Mark Zhukovsky Date: Sat, 16 Oct 2021 10:36:07 +0400 Subject: [PATCH 1/2] add index_of, last_index_of, for_each methods and tests --- py_linq/py_linq.py | 36 ++++++++++++++++++++++++++++++++++++ tests/test_functions.py | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/py_linq/py_linq.py b/py_linq/py_linq.py index bac46e8..65d5553 100644 --- a/py_linq/py_linq.py +++ b/py_linq/py_linq.py @@ -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): + """ + 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): + """ + 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. diff --git a/tests/test_functions.py b/tests/test_functions.py index 769e8f0..f9cfc77 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -1,5 +1,7 @@ +from io import StringIO from py_linq.py_linq import SelectEnumerable, WhereEnumerable from unittest import TestCase +from unittest.mock import patch from py_linq import Enumerable from tests import _empty, _simple, _complex, _locations from py_linq.exceptions import ( @@ -94,6 +96,20 @@ def test_select(self): .to_list(), ) + def test_for_each(self): + + with patch("sys.stdout", new=StringIO()) as fake_out: + self.simple.for_each(print) + self.assertEqual(fake_out.getvalue(), "1\n2\n3\n") + + with patch("sys.stdout", new=StringIO()) as fake_out: + self.complex.for_each(lambda x: print(x["value"])) + self.assertEqual(fake_out.getvalue(), "1\n2\n3\n") + + output = [] + self.complex.for_each(lambda x: output.append(x)) + self.assertListEqual(output, self.complex.to_list()) + def test_min(self): self.assertRaises(NoElementsError, self.empty.min) self.assertEqual(1, self.simple.min()) @@ -200,6 +216,16 @@ def test_median(self): self.assertEqual(median, self.simple.median()) self.assertEqual(median, self.complex.median(lambda x: x["value"])) + def test_index_of(self): + self.assertEqual(1, self.simple.index_of(2)) + self.assertEqual(1, self.complex.index_of({"value": 2})) + + def test_last_index_of(self): + self.assertEqual(4, self.simple.concat(Enumerable(_simple)).last_index_of(2)) + self.assertEqual( + 4, self.complex.concat(Enumerable(_complex)).last_index_of({"value": 2}) + ) + def test_skip(self): self.assertListEqual([], Enumerable().skip(2).to_list()) self.assertListEqual([], Enumerable([1, 2, 3]).skip(3).to_list()) @@ -849,11 +875,13 @@ def test_to_dictionary(self): test = Enumerable(["ab", "bc", "cd", "de"]).to_dictionary(lambda t: t[0]) self.assertDictEqual(test, {"a": "ab", "b": "bc", "c": "cd", "d": "de"}) - test = Enumerable([ - [0, 1, 2], - [3, 4, 5], - [6, 7, 8] - ]).to_dictionary(lambda t: t[0], lambda t: t[1:]) + test = Enumerable( + [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + ] + ).to_dictionary(lambda t: t[0], lambda t: t[1:]) self.assertDictEqual(test, {0: [1, 2], 3: [4, 5], 6: [7, 8]}) def test_zip(self): From 8fcb299af32786ded3f86b487b0f232e09d4c02a Mon Sep 17 00:00:00 2001 From: Mark Zhukovsky Date: Sat, 16 Oct 2021 10:37:43 +0400 Subject: [PATCH 2/2] add index_of, last_index_of, for_each methods to docs --- docs/for-each.md | 34 ++++++++++++++++++++ docs/index-of.md | 33 +++++++++++++++++++ docs/index.md | 73 ++++++++++++++++++++++--------------------- docs/last-index-of.md | 36 +++++++++++++++++++++ 4 files changed, 141 insertions(+), 35 deletions(-) create mode 100644 docs/for-each.md create mode 100644 docs/index-of.md create mode 100644 docs/last-index-of.md diff --git a/docs/for-each.md b/docs/for-each.md new file mode 100644 index 0000000..c6de0df --- /dev/null +++ b/docs/for-each.md @@ -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** + +

+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
+
+
\ No newline at end of file diff --git a/docs/index-of.md b/docs/index-of.md new file mode 100644 index 0000000..b816a2a --- /dev/null +++ b/docs/index-of.md @@ -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** + +

+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
+
\ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 384f1e0..5056d4d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -42,38 +42,41 @@ Once you have created an `Enumerable` instance, the LINQ methods will become ava 11. [intersect](/py-enumerable/intersect) 12. [except_](/py-enumerable/except) 13. [to_list](/py-enumerable/to-list) -14. [count](/py-enumerable/count) -15. [sum](/py-enumerable/sum) -16. [min](/py-enumerable/min) -17. [max](/py-enumerable/max) -18. [avg](/py-enumerable/avg) -19. [median](/py-enumerable/median) -20. [any](/py-enumerable/any) -21. [element_at](/py-enumerable/element-at) -22. [element_at_or_default](/py-enumerable/element-at-or-default) -23. [first](/py-enumerable/first) -24. [first_or_default](/py-enumerable/first-or-default) -25. [last](/py-enumerable/last) -26. [last_or_default](/py-enumerable/last-or-default) -27. [contains](/py-enumerable/contains) -28. [group_by](/py-enumerable/group-by) -29. [distinct](/py-enumerable/distinct) -30. [group_join](/py-enumerable/group-join) -31. [union](/py-enumerable/union) -32. [all](/py-enumerable/all) -33. [aggregate](/py-enumerable/aggregate) -34. [append](/py-enumerable/append) -35. [prepend](/py-enumerable/prepend) -36. [empty](/py-enumerable/empty) -37. [range](/py-enumerable/range) -38. [repeat](/py-enumerable/repeat) -39. [reverse](/py-enumerable/reverse) -40. [skip_last](/py-enumerable/skip_last) -41. [skip_while](/py-enumerable/skip_while) -42. [take_last](/py-enumerable/take_last) -43. [take_while](/py-enumerable/take_while) -44. [zip](/py-enumerable/zip) -45. [default_if_empty](/py-enumerable/default_if_empty) -46. [single](/py-enumerable/single) -47. [single_or_default](/py-enumerable/single-or-default) -48. [to_dictionary](/py-enumerable/to-dictionary) \ No newline at end of file +14. [for_each](/py-enumerable/for-each) +15. [count](/py-enumerable/count) +16. [sum](/py-enumerable/sum) +17. [min](/py-enumerable/min) +18. [max](/py-enumerable/max) +19. [avg](/py-enumerable/avg) +20. [median](/py-enumerable/median) +21. [any](/py-enumerable/any) +22. [index_of](/py-enumerable/index-of) +23. [last_index_of](/py-enumerable/last-index-of) +24. [element_at](/py-enumerable/element-at) +25. [element_at_or_default](/py-enumerable/element-at-or-default) +26. [first](/py-enumerable/first) +27. [first_or_default](/py-enumerable/first-or-default) +28. [last](/py-enumerable/last) +29. [last_or_default](/py-enumerable/last-or-default) +30. [contains](/py-enumerable/contains) +31. [group_by](/py-enumerable/group-by) +32. [distinct](/py-enumerable/distinct) +33. [group_join](/py-enumerable/group-join) +34. [union](/py-enumerable/union) +35. [all](/py-enumerable/all) +36. [aggregate](/py-enumerable/aggregate) +37. [append](/py-enumerable/append) +38. [prepend](/py-enumerable/prepend) +39. [empty](/py-enumerable/empty) +40. [range](/py-enumerable/range) +40. [repeat](/py-enumerable/repeat) +41. [reverse](/py-enumerable/reverse) +42. [skip_last](/py-enumerable/skip_last) +43. [skip_while](/py-enumerable/skip_while) +44. [take_last](/py-enumerable/take_last) +45. [take_while](/py-enumerable/take_while) +46. [zip](/py-enumerable/zip) +47. [default_if_empty](/py-enumerable/default_if_empty) +48. [single](/py-enumerable/single) +49. [single_or_default](/py-enumerable/single-or-default) +50. [to_dictionary](/py-enumerable/to-dictionary) \ No newline at end of file diff --git a/docs/last-index-of.md b/docs/last-index-of.md new file mode 100644 index 0000000..bb0edb2 --- /dev/null +++ b/docs/last-index-of.md @@ -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** + +

+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
+
\ No newline at end of file