{"id":19094907,"url":"https://github.com/zahash/amnis","last_synced_at":"2025-04-30T14:04:39.411Z","repository":{"id":53324678,"uuid":"441825617","full_name":"zahash/amnis","owner":"zahash","description":"Java like Streams Api for Python","archived":false,"fork":false,"pushed_at":"2023-10-25T07:28:52.000Z","size":79,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-13T05:17:10.147Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zahash.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-26T06:32:21.000Z","updated_at":"2024-08-29T17:15:57.000Z","dependencies_parsed_at":"2023-07-17T14:32:13.455Z","dependency_job_id":"ef6ff114-647f-4853-a0f0-045aa17f26c0","html_url":"https://github.com/zahash/amnis","commit_stats":null,"previous_names":["zahash/amnis"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zahash%2Famnis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zahash%2Famnis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zahash%2Famnis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zahash%2Famnis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zahash","download_url":"https://codeload.github.com/zahash/amnis/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251716695,"owners_count":21632173,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T03:32:01.634Z","updated_at":"2025-04-30T14:04:39.330Z","avatar_url":"https://github.com/zahash.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003cdiv align=\"center\"\u003e\n\n\u003cpre\u003e\n █████╗ ███╗   ███╗███╗   ██╗██╗███████╗\n██╔══██╗████╗ ████║████╗  ██║██║██╔════╝\n███████║██╔████╔██║██╔██╗ ██║██║███████╗\n██╔══██║██║╚██╔╝██║██║╚██╗██║██║╚════██║\n██║  ██║██║ ╚═╝ ██║██║ ╚████║██║███████║\n╚═╝  ╚═╝╚═╝     ╚═╝╚═╝  ╚═══╝╚═╝╚══════╝\n----------------------------------------\nJava like Streams Api for Python\n\u003c/pre\u003e\n\n[![PyPI](https://img.shields.io/pypi/v/amnis.svg)](https://pypi.org/project/amnis/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nStreams are lazy evaluated -- That is, evaluation starts only when a terminal operation is applied\n\n\u003c/div\u003e\n\n## Installation\n\npip install this repo.\n(Note: Incompatible with Python 2.x)\n\n```sh\npip3 install amnis\n```\n\n(or)\n\n```sh\npip install amnis\n```\n\n\n# Usage examples\n\n\n## `allmatch`\n\nCheck if all elements in the stream match a condition.\n\nThis method returns `True` if all elements in the stream satisfy the condition\ndefined by the provided function `fn`. If any element does not satisfy the\ncondition, `False` is returned.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([\"cat\", \"fat\", \"rat\"])\n            .allmatch(lambda x: \"at\" in x))\n\n# True\n\nresult = (Stream([\"cat\", \"dog\", \"rat\"])\n            .allmatch(lambda x: \"at\" in x))\n\n# False\n```\n\n\n## `anymatch`\n\nCheck if any element in the stream matches a condition.\n\nThis method returns `True` if at least one element in the stream satisfies the\ncondition defined by the provided function `fn`. If no element satisfies the\ncondition, `False` is returned.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([\"cat\", \"dog\", \"rat\"])\n            .anymatch(lambda x: \"at\" in x))\n\n# True\n\nresult = (Stream([\"cat\", \"dog\", \"rat\"])\n            .anymatch(lambda x: \"z\" in x))\n\n# False\n```\n\n\n## `catch`\n\nHandle exceptions while iterating through the stream.\n\nThis method returns a new Stream that applies the provided exception handler\nfunction `handler` to handle exceptions of a specific type `err_type` while\niterating through the stream. The handler can return a value to continue\niteration, or raise a different exception to propagate it.\n\n```Python\n\n# =======\n# Logging\n# =======\n\nfrom amnis import Stream\nimport logging\n\ndef handler(err):\n    logging.error(err)\n\nresult = (Stream(['a', 'b', 'c', 10, 'd'])\n    .map(lambda x: x.upper())\n    .catch(handler)\n    .collect(list))\n\n# \u003e\u003e\u003e ERROR:root:'int' object has no attribute 'upper'\n# result = ['A', 'B', 'C', None, 'D']\n```\n\n```Python\n\n# =======================\n# Multiple Error Handling\n# =======================\n\nfrom amnis import Stream\nimport logging\n\ndef handle_upper(err):\n    logging.error(err)\n\ndef handle_index_out_of_bounds(err):\n    logging.warning(err)\n\nresult = (Stream(['ab', 'cd', 'e', 10, 'fg'])\n    .map(lambda x: x.upper())\n    .catch(handle_upper)\n    .map(lambda x: x[1])\n    .catch(handle_index_out_of_bounds)\n    .collect(list))\n\n# \u003e\u003e\u003e WARNING:root:string index out of range\n# \u003e\u003e\u003e ERROR:root:'int' object has no attribute 'upper'\n# result = ['B', 'D', 'G']\n```\n\n```Python\n\n# ===================\n# Replace Error Value\n# ===================\n\nfrom amnis import Stream\n\ndef sqrt(x):\n    if x \u003c 0:\n        raise ValueError(x)\n    return x ** 0.5\n\ndef handler_neg_sqrt(err):\n    (value,) = err.args\n    return value * 1000\n\nresult = (Stream([4, 9, -3, 16])\n    .map(sqrt)\n    .catch(handler_neg_sqrt)\n    .collect(list))\n\n# [2.0, 3.0, -3000, 4.0]\n```\n\n```Python\n\n# =======================\n# Specific Error Handling\n# =======================\n\ndef err_fn(x):\n    if x == 'a':\n        raise ValueError(x)\n    if x == 'b':\n        raise KeyError(x)\n    return x\n\ndef handle_value_err(err):\n    (value,) = err.args\n    return value * 2\n\ndef handle_key_err(err):\n    (value,) = err.args\n    return value * 4\n\nresult = (Stream(['e', 'a', 'g', 'd', 'b'])\n    .map(err_fn)\n    .catch(handle_value_err, ValueError)\n    .catch(handle_key_err, KeyError)\n    .collect(list))\n\n# ['e', 'aa', 'g', 'd', 'bbbb']\n```\n\n\n## `collect`\n\nCollect the elements in the stream into a collection.\n\nThis method collects the elements in the stream and returns them as a collection,\ndetermined by the provided collector function. The default collector is `iter`,\nwhich returns an iterable containing the elements.\n\n```Python\nfrom amnis import Stream\n\nresult = Stream([1, 2, 3]).collect(list)\n\n# [1, 2, 3]\n```\n\n\n## `count`\n\nCount the number of elements in the stream.\n\nThis method returns the total number of elements in the stream.\n\n```Python\nfrom amnis import Stream\n\nresult = Stream(['a', 'b', 'c']).count()\n\n# 3\n```\n\n\n## `distinct`\n\nRemove duplicate elements from the stream.\n\nThis method returns a new Stream containing only the distinct elements from the\noriginal stream. Duplicate elements are eliminated, and only the first occurrence\nis retained.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([3, 2, 3, 1, 3, 2, 2])\n    .distinct()\n    .collect(list))\n\n# [3, 2, 1]\n```        \n\n\n## `filter`\n\nFilter elements in the stream based on a given condition.\n\nThis method filters the elements in the stream, retaining only those that satisfy\nthe provided condition defined by the function `fn`.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([1, 2, 3])\n    .filter(lambda x: x \u003e 1)\n    .collect(list))\n\n# [2, 3]\n```        \n\n\n## `find`\n\nFind the first element that matches a condition in the stream.\n\nThis method returns the first element from the stream that satisfies the condition\ndefined by the provided function `fn`. If no matching element is found, `None` is returned.\n\n```Python\nfrom amnis import Stream\n\nresult = Stream([5, 4, 3, 2, 1]).find(lambda x: x % 2 == 0)\n\n# 4\n```\n\n\n## `first`\n\nGet the first element from the stream.\n\nThis method returns the first element from the stream, or `None` if the stream is empty.\n\n```Python\nfrom amnis import Stream\n\nresult = Stream([1, 2, 3]).first()\n\n# 1\n```\n\n\n## `flatmap`\n\nApply a function to each element and flatten the results into a single stream.\n\nThis method applies the provided function `fn` to each element in the stream and\nthen flattens the resulting iterable of each function call into a single stream.\n\nflatmap is exactly the same as doing map and flatten\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([\"it's Sunny in\", \"\", \"California\"])\n            .flatmap(lambda s: s.split(\" \"))\n            .collect(list))\n\n# [\"it's\", \"Sunny\", \"in\", \"\", \"California\"]\n```\n\n\n## `flatten`\n\nFlatten a stream of nested iterables into a single stream.\n\nThis method flattens a stream that contains nested iterables, such as lists or\ntuples, into a single stream of individual elements.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([\n            [1, 2, 3],\n            [],\n            [4, 5]\n        ])\n            .flatten()\n            .collect(list))\n\n# [1, 2, 3, 4, 5]\n```\n\n\n## `foreach`\n\nApply a function to each element in the stream.\n\nThis method applies the provided function `fn` to each element in the stream.\nThe function can have side effects.\n\n```Python\nfrom amnis import Stream\n\nStream([1, 2, 3]).foreach(lambda x: print(x))\n\n# \u003e\u003e\u003e 1\n# \u003e\u003e\u003e 2\n# \u003e\u003e\u003e 3\n```\n\n\n## `group`\n\nGroup elements in the stream based on keys and values.\n\nThis method groups elements in the stream based on keys and values determined by\nthe provided key and value functions. The grouping logic is defined by the provided\ngrouper object, which specifies how values are combined for each key.\n\nParameters:\n    `key_fn`: A function that takes an element of type `T` and\n        returns a key of type `U` to group the elements by.\n    `val_fn`: A function that takes an element of type `T` and\n        returns a value of type `V` associated with the element.\n    `grouper`: A Grouper object that specifies how values are combined for each key.\n        The Grouper should have `collection` and `grouper_fn` attributes.\n\nReturns:\n    dict: A dictionary where keys are the result of the key function and values\n    are the results of applying the grouper function to the associated values.\n\n```Python\nfrom amnis import Stream, Grouper\n\nresult = (Stream([\"apple\", \"banana\", \"cherry\"])\n            .group(\n                key_fn=lambda word: len(word),\n                val_fn=lambda word: word.upper(),\n                grouper=Grouper(list, grouper_fn=lambda l, item: l.append(item))\n            ))\n\n# {5: ['APPLE'], 6: ['BANANA', 'CHERRY']}\n\nclass Person:\n    def __init__(self, name, age):\n        self.name, self.age = name, age\n\npeople = [\n    Person(\"jack\", 20),\n    Person(\"jack\", 30),\n    Person(\"jill\", 25),\n    Person(\"jack\", 40)\n]\n\nresult = (Stream(people)\n            .group(\n                key_fn=lambda p: p.name,\n                val_fn=lambda p: p.age,\n                grouper=Grouper(list, grouper_fn=lambda l, item: l.append(item))\n            ))\n\n# {\n#   \"jack\": [20, 30, 40],\n#   \"jill\": [25]\n# }\n\nresult = (Stream(people)\n            .group(\n                key_fn=lambda p: p.name,\n                val_fn=lambda p: p.age,\n                grouper=Grouper(str, grouper_fn=lambda s, item: f\"{s}--{item}\" if s else f\"{item}\")\n            ))\n\n# {\n#   \"jack\": \"20--30--40\",\n#   \"jill\": \"25\"\n# }\n```\n\n\n## `inspect`\n\nApply a function to each element in the stream while preserving the stream's contents.\n\nThis method returns a new Stream that applies the provided function `fn` to each\nelement in the original stream. The function is called for every element, and it can\nhave side effects. The original elements remain unchanged, and the new Stream still\ncontains the same elements.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([1, 2, 3])\n          .inspect(lambda x: print(f\"before map : {x}\"))\n          .map(lambda x: x * 3)\n          .inspect(lambda x: print(f\"after map  : {x}\"))\n          .collect(list))\n\n# \u003e\u003e\u003e before map : 1\n# \u003e\u003e\u003e after map  : 3\n# \u003e\u003e\u003e before map : 2\n# \u003e\u003e\u003e after map  : 6\n# \u003e\u003e\u003e before map : 3\n# \u003e\u003e\u003e after map  : 9\n# result = [3, 6, 9]\n```\n\n\n## `last`\n\nGet the last element from the stream.\n\nThis method returns the last element from the stream, or `None` if the stream is empty.\n\n```Python\nfrom amnis import Stream\n\nresult = Stream([1, 2, 3]).last()\n\n# 3\n```\n\n\n## `limit`\n\nLimit the number of elements in the stream.\n\nThis method returns a new Stream containing, at most, the first `n` elements from\nthe original stream. If `n` is greater than the total number of elements, all\nelements from the original stream will be included.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream(range(100))\n    .limit(10)\n    .collect(list))\n\n# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n```\n\n\n## `map`\n\nApply a function to each element in the stream.\n\nThis method applies the provided function `fn` to each element in the stream \nand returns a new Stream containing the transformed values.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([1, 2, 3])\n    .map(lambda x: x * 2)\n    .collect(list))\n\n# [2, 4, 6]\n```\n\n\n## `max`\n\nFind the maximum element in the stream.\n\nThis method returns the maximum element from the stream, based on the natural\nordering of the elements or a custom sorting key defined by the `key` parameter.\n\n```Python\nfrom amnis import Stream\n\nresult = Stream([\"a\", \"bbbbb\", \"cc\"]).max()\n\n# \"cc\"\n\nresult = Stream([\"a\", \"bbbbb\", \"cc\"]).max(lambda x: len(x))\n\n# \"bbbbb\"\n```\n\n\n## `min`\n\nFind the minimum element in the stream.\n\nThis method returns the minimum element from the stream, based on the natural\nordering of the elements or a custom sorting key defined by the `key` parameter.\n\n```Python\nfrom amnis import Stream\n\nresult = Stream([\"cc\", \"aaaaa\", \"b\"]).min()\n\n# \"aaaaa\"\n\nresult = Stream([\"cc\", \"aaaaa\", \"b\"]).min(lambda x: len(x))\n\n# \"b\"\n```\n\n\n## `nomatch`\n\nCheck if no elements in the stream match a condition.\n\nThis method returns `True` if none of the elements in the stream satisfy the condition\ndefined by the provided function `fn`. If any element satisfies the condition, `False`\nis returned.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([3, 5, 7])\n          .nomatch(lambda x: x % 2 == 0))\n\n# True\n\nresult = (Stream([3, 4, 7])\n          .nomatch(lambda x: x % 2 == 0))\n\n# False\n```\n\n\n## `nth`\n\nGet the element at the nth position in the stream.\n\nThis method returns the element at the specified position `n` in the stream. If the\nposition is out of bounds, `None` is returned. The position index is zero-based.\nNegative indexes are not supported.\n\n```Python\nfrom amnis import Stream\n\nresult = Stream([1, 2, 3]).nth(1)\n\n# 2\n```\n\n\n## `par_map`\n\nParallel version of the `map` method for CPU-bound operations.\nThe results may not be in the same order as the original stream due\nto the parallel execution.\n\n**IMPORTANT**: Lambda functions are not allowed as `fn` since they are not easily\nserializable for parallel execution. Define your mapping function as a regular\nnamed function.\n\nSee the documentation for the `map` method for more details.\n\n```Python\nfrom amnis import Stream\n\ndef times2(x):\n    return x * 2\n\nresult = (Stream([1, 2, 3])\n    .par_map(times2)\n    .collect(list))\n\n# [2, 4, 6]\n```\n\n\n## `reduce`\n\nReduce the elements in the stream to a single value.\n\nThis method applies the provided binary function `fn` cumulatively to the elements\nof the stream, from left to right, and returns a single accumulated result. An\ninitial value can be provided to start the accumulation; otherwise, the first element\nof the stream is used as the initial value.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([1, 2, 3])\n    .reduce(lambda x, y: x + y))\n\n# 6\n\nimport operator\n\nresult = (Stream([1, 2, 3])\n    .reduce(operator.add, initial=20))\n\n# 26\n```\n\n\n## `skip`\n\nSkip the first `n` elements in the stream.\n\nThis method returns a new Stream containing the elements from the original stream\nafter skipping the first `n` elements. If `n` is greater than the total number of\nelements, an empty stream will be returned.\n\nStream([1, 2, 3]).skip(2)\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([1, 2, 3])\n    .skip(2)\n    .collect(list))\n\n# [3]\n```\n\n\n## `skipwhile`\n\nCreate a new stream by skipping elements while a condition is met.\n\nThis method returns a new Stream that starts including elements from the original\nstream as soon as the provided condition defined by the function `fn` evaluates to False.\nElements before the first one that does not satisfy the condition are skipped.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([1, 2, 2, 3, 5, 3, 2, 3, 5])\n            .skipwhile(lambda x: x \u003c 3)\n            .collect(list))\n\n# [3, 5, 3, 2, 3, 5]\n```\n\n\n## `sorted`\n\nSort the elements in the stream.\n\nThis method returns a new Stream containing the elements from the original stream,\nsorted in ascending order by default. A custom sorting key can be provided using\nthe `key` parameter.\n\n**WARNING**: Sorting is an eager operation and requires all elements to be loaded into memory.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([5, 4, 3, 2, 1])\n            .sorted()\n            .collect(list))\n\n# [1, 2, 3, 4, 5]\n\nresult = (Stream([(1, 4), (2, 3), (3, 2), (4, 1)])\n          .sorted(key=lambda p: p[1])\n          .collect(list))\n\n# [(4, 1), (3, 2), (2, 3), (1, 4)]\n```\n\n\n## `takewhile`\n\nCreate a new stream with elements while a condition is met.\n\nThis method returns a new Stream that includes elements from the original stream\nas long as the provided condition defined by the function `fn` evaluates to True.\nOnce an element is encountered that does not satisfy the condition, the new stream\nstops including further elements.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([1, 2, 2, 4, 5, 3, 2, 3, 5])\n            .takewhile(lambda x: x \u003c 5)\n            .collect(list))\n\n# [1, 2, 2, 4]\n```\n\n\n## `window`\n\nCreate a sliding window over the stream.\n\nThis method returns a new Stream where each element is a tuple containing the elements\nof the original stream that form a sliding window of the specified size `window_size`.\nThe elements within each window are ordered as they appear in the stream.\n\n```Python\nfrom amnis import Stream\n\nresult = (Stream([1, 2, 3, 4, 5])\n            .window(3)\n            .collect(list))\n\n# [\n#     (1, 2, 3),\n#     (2, 3, 4),\n#     (3, 4, 5),\n# ]\n\nresult = (Stream([1, 2, 3, 4, 5])\n            .window(3)\n            .map(lambda w: w[0]+w[1]+w[2])\n            .collect(list))\n\n# [6, 9, 12]\n```\n\n\n\n## Development setup\n\nClone this repo and install packages listed in requirements.txt\n\n```sh\npip3 install -r requirements.txt\n```\n\n## Meta\n\nM. Zahash - zahash.z@gmail.com\n\nDistributed under the MIT license. See `LICENSE` for more information.\n\n[https://github.com/zahash/](https://github.com/zahash/)\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/zahash/amnis/fork\u003e)\n2. Create your feature branch (`git checkout -b feature/fooBar`)\n3. Commit your changes (`git commit -am 'Add some fooBar'`)\n4. Push to the branch (`git push origin feature/fooBar`)\n5. Create a new Pull Request\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzahash%2Famnis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzahash%2Famnis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzahash%2Famnis/lists"}