{"id":22295913,"url":"https://github.com/mkalioby/leopards","last_synced_at":"2025-09-12T00:33:49.323Z","repository":{"id":63030400,"uuid":"564707723","full_name":"mkalioby/leopards","owner":"mkalioby","description":"Quicky query your Python lists","archived":false,"fork":false,"pushed_at":"2025-01-30T14:19:17.000Z","size":59,"stargazers_count":258,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-13T04:40:31.878Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/mkalioby.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":"2022-11-11T10:03:12.000Z","updated_at":"2025-04-27T07:35:20.000Z","dependencies_parsed_at":"2025-01-14T16:13:29.941Z","dependency_job_id":"57b00b83-7874-43fb-8459-a1c3a9020be8","html_url":"https://github.com/mkalioby/leopards","commit_stats":{"total_commits":27,"total_committers":1,"mean_commits":27.0,"dds":0.0,"last_synced_commit":"a02b2e308095e59fad68f9aecda7584ff4b7a3d9"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkalioby%2Fleopards","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkalioby%2Fleopards/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkalioby%2Fleopards/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkalioby%2Fleopards/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mkalioby","download_url":"https://codeload.github.com/mkalioby/leopards/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254453667,"owners_count":22073618,"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-12-03T17:43:34.013Z","updated_at":"2025-05-16T02:10:03.244Z","avatar_url":"https://github.com/mkalioby.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Leopards\n\n[![PyPI version](https://badge.fury.io/py/leopards.svg)](https://badge.fury.io/py/leopards)\n[![Python Versions](https://img.shields.io/pypi/pyversions/leopards.svg)](https://img.shields.io/pypi/pyversions/leopards.svg)\n![Coverage](https://img.shields.io/badge/coverage-100%25-success)\n![build status](https://github.com/mkalioby/leopards/actions/workflows/workflow.yml/badge.svg)\n\nLeopards is a way to query list of dictionaries or objects as if you are filtering in  DBMS. \nYou can get dicts/objects that are matched by OR, AND or NOT or all of them.\nAs you can see in the comparison they are much faster than Pandas.\n\n\n## Installation\n\n```shell\npip install leopards\n```\n\n## Usage\n\n```python\nfrom leopards import Q\nl = [{\"name\":\"John\",\"age\":\"16\"}, {\"name\":\"Mike\",\"age\":\"19\"},{\"name\":\"Sarah\",\"age\":\"21\"}]\nfiltered= Q(l,{'name__contains':\"k\", \"age__lt\":20})\nprint(list(filtered))\n```\noutput\n```python\n[{'name': 'Mike', 'age': '19'}]\n```\n\nThe above filtration can be written as\n\n```python\nfrom leopards import Q\n\nl = [{\"name\": \"John\", \"age\": \"16\"}, {\"name\": \"Mike\", \"age\": \"19\"}, {\"name\": \"Sarah\", \"age\": \"21\"}]\nfiltered = Q(l, name__contains=\"k\", age__lt=20)\n\n```\n\n**Notes:** \n1. `Q` returns an iterator which can be converted to a list by calling `list`.\n2. Even though, age was `str` in the dict, as the value of in the query dict was `int`, Leopards converted the value in dict automatically to match the query data type. This behaviour can be stopped by passing `False` to `convert_types` parameter.\n\n## Supported filters\n* `eq`: equals and this default filter\n* `gt`: greater than.\n* `gte`: greater than or equal.\n* `lt`: less than \n* `lte`: less than or equal \n* `in`: the value in a list of a tuple.\n    * e.g.  age__in=[10,20,30]\n* `contains`: contains a substring as in the example.\n* `icontains`: case-insensitive `contains`.\n* `startswith`: checks if a value starts with a query strings.\n* `istartswith`: case-insensitive `startswith`.\n* `endswith`: checks if a value ends with a query strings.\n* `iendswith`: case-insensitive `endswith`.\n* `isnull`:  checks if the value matches any of NULL_VALUES which are `('', '.', None, \"None\", \"null\", \"NULL\")`\n  * e.g. `filter__isnull=True` or `filter__isnull=False`\n\nFor `eq`,`gt`,`gte`,`lt`,`lte`, `in`, `contains`, `icontains`, `startswith`,`istartswith`, `endswith` and `iendswith`, you can add a `n` to negate the results. e.g  `nin` which is equivalent to `not in` \n\n   \n## Advanced examples\nThis section will cover the use of `OR`, `AND` and `NOT`\n\n### Usage of `OR`\n`OR` or `__or__` takes a list of dictionaries to evaluate and returns with the first `True`.\n\n```python\nfrom leopards import Q\n\nl = [{\"name\": \"John\", \"age\": \"16\"}, {\"name\": \"Mike\", \"age\": \"19\"}, {\"name\": \"Sarah\", \"age\": \"21\"}]\nfiltered = Q(l, {\"OR\": [{\"name__contains\": \"k\"}, {\"age__gte\": 21}]})\nprint(list(filtered))\n```\noutput\n```python\n[{'name': 'Mike', 'age': '19'}, {'name': 'Sarah', 'age': '21'}]\n```\n\n### Usage of `NOT`\n`NOT` or `__not__` takes a dict for query run.\n\n```python\nfrom leopards import Q\n\nl = [{\"name\": \"John\", \"age\": \"16\"}, {\"name\": \"Mike\", \"age\": \"19\"}, {\"name\": \"Sarah\", \"age\": \"21\"}]\nfiltered = Q(l, {\"age__gt\": 15, \"NOT\": {\"age__eq\": 19}})\nprint(list(filtered))\n```\noutput\n```python\n[{'name': 'John', 'age': '16'}, {'name': 'Sarah', 'age': '21'}]\n```\n\n### Usage of `AND`\n`AND` or `__and__` takes a list of dict for query run, returns with the first `False`.\n\n```python\nfrom leopards import Q\n\nl = [{\"name\": \"John\", \"age\": \"16\"}, {\"name\": \"Mike\", \"age\": \"19\"}, {\"name\": \"Sarah\", \"age\": \"21\"}]\nfiltered = Q(l, {\"__and__\": [{\"age__gte\": 15}, {\"age__lt\": 21}]})\nprint(list(filtered))\n```\noutput\n```python\n[{'name': 'John', 'age': '16'}, {'name': 'Mike', 'age': '19'}]\n```\n\n## Aggregating Data\n\nYou  can run the following aggregations\n* Count\n* Max\n* Min\n* Sum\n* Avg\n\n### Count\n\nFind the count of certain aggregated column\n```python\nl = [{\"name\": \"John\", \"age\": \"16\"}, {\"name\": \"Mike\", \"age\": \"19\"}, {\"name\": \"Sarah\", \"age\": \"21\"},{\"name\":\"John\",\"age\":\"19\"}]\nfrom leopards import Count\ncount = Count(l,['age'])\n```\noutput\n```python\n[{\"age\":\"16\",\"count\":1},{\"age\":\"19\",\"count\":2}, {\"age\":\"21\",\"count\":1}]\n```\n\n### Max\n\nFind the Max value for a certain column in  certain aggregated columns\n```python\nl = [{\"name\": \"John\", \"age\": \"16\"}, {\"name\": \"Mike\", \"age\": \"19\"}, {\"name\": \"Sarah\", \"age\": \"21\"},{\"name\":\"John\",\"age\":\"19\"}]\nfrom leopards import Max\ncount = Max(l,\"age\",['name'],dtype=int)\n```\noutput\n```python\n[{'name': 'John', 'age': '19'}, {'name': 'Mike', 'age': '19'}, {'name': 'Sarah', 'age': '21'}]\n```\n\n**Notes:**\n* If you don't pass the aggregation columns, the maximum will be found across dataset.\n* You can pass the datatype of the column to convert it on the fly while evaluating\n```python\nl = [{\"name\": \"John\", \"age\": \"16\"}, {\"name\": \"Mike\", \"age\": \"19\"}, {\"name\": \"Sarah\", \"age\": \"21\"},{\"name\":\"John\",\"age\":\"19\"}]\nfrom leopards import Max\nm = Max(l,\"age\",dtype=int)\n```\n\noutput\n```python\n[{'age': 21}]\n```\n\n\n### Min\n\nFind the Max value for a certain column in  certain aggregated columns\n```python\nl = [{\"name\": \"John\", \"age\": \"16\"}, {\"name\": \"Mike\", \"age\": \"19\"}, {\"name\": \"Sarah\", \"age\": \"21\"},{\"name\":\"John\",\"age\":\"19\"}]\nfrom leopards import Min\nm = Min(l,\"age\",['name'])\n```\noutput\n```python\n[{'name': 'John', 'age': '16'}, {'name': 'Mike', 'age': '19'}, {'name': 'Sarah', 'age': '21'}]\n```\n**Note:** \n* If you don't pass the aggregation columns, the min will be found across dataset.\n* You can pass the datatype of the column to convert it on the fly while evaluating\n\n\n## Sum and Avg\n\nLike Min and Max but only works with integers and floats.\n\n## Comparison with Pandas\n\nThis is done on Python 3.8 running on Ubuntu 22.04 on i7 11th generation and 32 GB of RAM.\n\n| Comparison                                                  | Pandas   | Leopards    |\n|-------------------------------------------------------------|----------|-------------|\n| Package Size     \u003cbr/\u003e (Lower is better)                    | 29.8 MB  | **7.5 KB**  |\n| import Time (Worst) \u003cbr/\u003e (Lower is better)                 | 146 ms   | **1.05 ms** |\n| load 10k CSV lines\u003cbr/\u003e (Lower is better) \u003csup\u003e[1]\u003c/sup\u003e    | 0.295s   | **0.138s**  |\n| get first matched record\u003cbr/\u003e (Lower is better)             | 0.310s   | **0.017s**  |\n| print all filtered records (10/10k) \u003cbr/\u003e (Lower is better) | 0.310s   | **0.137s**  | \n| filter by integers \u003cbr/\u003e(Lower is better)                   | 0.316s   | **0.138s**  |\n\n\u003csup\u003e[1]\u003c/sup\u003e This was loading the whole csv in memory which was for sake of fair comparison. \nNevertheless,  Leopards can work with DictReader as an iterable which executes in **0.014s**, then it handles line by line.\n\nThanks for [Asma Tahir](https://github.com/tahirasma) for Pandas stats.\n\n\n## Contributors \n\n* [saeedesmaili](https://github.com/saeedesmaili)\n\n## Tutorials\n\n* [Usage with different file types](https://dev.to/mkalioby/leopards-with-different-file-types-1d3)\n* [Work on CSV Files with Leopards](https://dev.to/mkalioby/working-with-csv-by-leopards-5bmd)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkalioby%2Fleopards","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmkalioby%2Fleopards","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkalioby%2Fleopards/lists"}