{"id":13514862,"url":"https://github.com/atom-community/zadeh","last_synced_at":"2025-04-11T19:33:11.556Z","repository":{"id":37027821,"uuid":"162220255","full_name":"atom-community/zadeh","owner":"atom-community","description":"Blazing fast library for fuzzy filtering, matching, and other fuzzy things!","archived":false,"fork":false,"pushed_at":"2025-03-23T23:08:58.000Z","size":5054,"stargazers_count":27,"open_issues_count":22,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-25T15:32:15.802Z","etag":null,"topics":["atom","fast","fuzzaldrin","fuzzy","fuzzy-search","hacktoberfest","native","node-modules"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/zadeh","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/atom-community.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"2018-12-18T02:46:06.000Z","updated_at":"2025-01-27T03:23:42.000Z","dependencies_parsed_at":"2024-06-18T22:42:32.757Z","dependency_job_id":"4cddfc8b-46ea-4a9a-a312-52a894052bd9","html_url":"https://github.com/atom-community/zadeh","commit_stats":null,"previous_names":["atom-ide-community/fuzzaldrin-plus-fast","atom-ide-community/zadeh"],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atom-community%2Fzadeh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atom-community%2Fzadeh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atom-community%2Fzadeh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atom-community%2Fzadeh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atom-community","download_url":"https://codeload.github.com/atom-community/zadeh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248467229,"owners_count":21108611,"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":["atom","fast","fuzzaldrin","fuzzy","fuzzy-search","hacktoberfest","native","node-modules"],"created_at":"2024-08-01T05:01:02.823Z","updated_at":"2025-04-11T19:33:11.523Z","avatar_url":"https://github.com/atom-community.png","language":"C++","funding_links":[],"categories":["C++"],"sub_categories":[],"readme":"Blazing fast library for fuzzy filtering, matching, and other fuzzy things!\n\n![CI](https://github.com/atom-ide-community/zadeh/workflows/CI/badge.svg)\n\n# Zadeh\n\nZadeh is a blazing fast library for fuzzy filtering, matching, and other fuzzy things. Zadeh is a multithreaded library written in C++ with the goal to search through a dataset with 1M entries in a few hundred milliseconds.\n\nThe name \"Zadeh\" refers to [Lofti Zadeh](https://en.wikipedia.org/wiki/Lotfi_A._Zadeh), the creator of fuzzy logic and fuzzy systems.\n\n### features\n\n- Fuzzy filter through an array of candidates (`StringArrayFilterer`)\n- Fuzzy filter through nested tree-like objects (`TreeFilterer`)\n- Special treatment for strings that have separators (space ` `, hyphen `-`, underline`_`)\n- Special treatment for path-like strings (string separated by `\\` or `//`)\n- give an array of indices at which the query matches the given string (`match`)\n- score the given string against the given query (`score`)\n- give an HTML/Markdown string that highlights the range for which the match happens (`wrap`)\n- Allows setting the candidates only once using `StringArrayFilterer` and `TreeFilterer` classes, and then perform `filter` multiple times, which is much more efficient than calling the `filter` or `filterTree` functions directly every time.\n- Bindings for Nodejs (more to come)\n\n# Usage\n\n## Usage from C++\n\nThis is a header-only library. Include `./src/zadeh.h` and build it in your application.\n\n`examples/example1.cpp`:\n\n```cpp\n#include \"../src/zadeh.h\"  // include zadeh.h\n#include \u003cstring\u003e\n#include \u003ciostream\u003e\n\nusing namespace std;\n\nint main() {\n  // the data to fuzzy search on\n  auto data = vector\u003cstring\u003e{\"eye\", \"why\", \"bi\"};\n\n  // setup StringArrayFilterer\n  auto strArrFilterer = zadeh::StringArrayFilterer\u003cvector\u003cstring\u003e, string\u003e{};\n  strArrFilterer.set_candidates(data);\n\n  // filter the indices that match the query\n  auto filtered_indices = strArrFilterer.filter_indices(\"ye\");\n\n  // print the filtered data\n  for (auto ind: filtered_indices) {\n    cout \u003c\u003c data[ind] \u003c\u003c '\\n';\n  }\n}\n```\n\nCmake file:\n\n```cmake\ncmake_minimum_required(VERSION 3.17)\n\nproject(example1 LANGUAGES CXX)\nadd_executable(example1 ./examples/example1.cpp)\ntarget_compile_features(example1 PRIVATE cxx_std_17)\n```\n\nBuild:\n\n```\ncmake -S . -B ./build \u0026\u0026 cmake --build ./build --config Debug\n```\n\n## Usage from Nodejs\n\nInstallation:\n\n```sh\nnpm install zadeh\n```\n\nTo import all the functions:\n\n```js\nimport * as zadeh from \"zadeh\"\n```\n\nor\n\n```js\nconst zadeh = require(\"zadeh\")\n```\n\n### StringArrayFilterer\n\n`StringArrayFilterer` is a class that allows setting the `candidates` only once and perform filtering on them multiple times. This is much more efficient than calling the `filter` function directly.\n\n\u003cdetails\u003e\n\u003csummary\u003e`StringArrayFilterer` API\u003c/summary\u003e\n\n```ts\nexport class StringArrayFilterer {\n  /**\n   * Make a `StringArrayFilterer` for the candidates that are going to be filtered.\n   *\n   * @param candidates An array of strings.\n   */\n  constructor(candidates?: Array\u003cstring\u003e)\n\n  /**\n   * Filter the already set array of strings\n   *\n   * @param query A string query to match each candidate against.\n   * @param options Options\n   * @returns Returns an array of candidates sorted by best match against the query.\n   */\n  filter(query: string, options: StringArrayFilterOptions = {}): Array\u003cstring\u003e\n\n  /**\n   * Filter the already set array of objects and get the indices of the chosen candidate\n   *\n   * @param query A string query to match the dataKey of each candidate against.\n   * @param options Options\n   * @returns Returns an array of numbers indicating the index of the chosen candidate sorted by the best match against the query.\n   */\n  filterIndices(query: string, options: StringArrayFilterOptions = {}): Array\u003cnumber\u003e\n\n  /**\n   * Allows setting the candidates (if changed or not set in the constructor).\n   *\n   * @param candidates An array of strings.\n   */\n  setCandidates(candidates: Array\u003cstring\u003e)\n}\n```\n\n\u003c/details\u003e\n\u003cbr/\u003e\n\n**Example**:\n\n```js\nconst { StringArrayFilterer } = require(\"zadeh\")\n\n// create class\nconst strArrFilterer = new StringArrayFilterer()\n\n// set the candidates\nstrArrFilterer.setCandidates([\"Call\", \"Me\", \"Maybe\"])\n\n// call filter multiple times\nstrArrFilterer.filter(\"me\")\nstrArrFilterer.filter(\"all\")\n```\n\n### ObjectArrayFilterer\n\nObjectArrayFilterer is a class that performs filtering on an array of objects based on a string stored in the given `dataKey` for each object\n\n\u003cdetails\u003e\n\u003csummary\u003e`ObjectArrayFilterer` API\u003c/summary\u003e\n\n```ts\nexport class ObjectArrayFilterer\u003cDataKey extends string | number = string\u003e {\n  /**\n   * Make an `ObjectArrayFilterer` for the candidates that are going to be filtered.\n   *\n   * @param candidates An array of objects.\n   * @param dataKey The key which is indexed for each object, and filtering is done based on the resulting string\n   */\n  constructor(candidates?: Array\u003cObjectWithKey\u003cDataKey\u003e\u003e, dataKey?: DataKey)\n\n  /**\n   * Filter the already set objects\n   *\n   * @param query A string query to match the dataKey of each candidate against.\n   * @param options Options\n   * @returns Returns an array of objects sorted by the best match against the query.\n   */\n  filter(query: string, options: ObjectArrayFilterOptions = {}): Array\u003cObjectWithKey\u003e\n\n  /**\n   * Filter the already set array of strings and get the indices of the chosen candidate\n   *\n   * @param query A string query to match each candidate against.\n   * @param options Options\n   * @returns Returns an array of numbers indicating the index of the chosen candidate sorted by the best match against the query.\n   */\n  filterIndices(query: string, options: StringArrayFilterOptions = {}): Array\u003cnumber\u003e\n\n  /**\n   * Allows setting the candidates (if changed or not set in the constructor).\n   *\n   * @param candidates An array of objects.\n   * @param dataKey The key which is indexed for each object, and filtering is done based on the resulting string\n   */\n  setCandidates(candidates: Array\u003cObjectWithKey\u003cDataKey\u003e\u003e, dataKey: DataKey)\n}\n```\n\n\u003c/details\u003e\n\u003cbr/\u003e\n\n**Example**:\n\n```js\nconst { ObjectArrayFilterer } = require(\"zadeh\")\n\nconst candidates = [\n  { name: \"Call\", id: 1 },\n  { name: \"Me\", id: 2 },\n  { name: \"Maybe\", id: 3 },\n]\n\n// create a class and set the candidates\nconst objArrFilterer = new ObjectArrayFilterer(candidates, \"name\") // filter based on their name\n\n// call filter multiple times\nobjArrFilterer.filter(\"me\") // [{ name: 'Me', id: 2 }, { name: 'Maybe', id: 3}] // finds two objects\nobjArrFilterer.filter(\"all\") // [{ name: 'Call', id: 1 }]\n```\n\n### TreeFilterer\n\nTreeFilterer filters the given query in the nodes of the given array of trees and returns an array of filtered\ntrees (or the indices of the filter candidates). A tree object is an object in which each entry stores the data in its `dataKey`, and it has (may have) some\nchildren (with a similar structure) in its `childrenKey`\n\n\u003cdetails\u003e\n\u003csummary\u003e`TreeFilterer` API\u003c/summary\u003e\n\n```ts\nexport class TreeFilterer\u003cDataKey extends string, ChildrenKey extends string\u003e {\n  /**\n   * The method to set an array of trees that are going to be filtered\n   *\n   * @param candidates An array of tree objects.\n   * @param dataKey The key of the object (and its children) which holds the data (defaults to `\"data\"`)\n   * @param childrenKey The key of the object (and its children) which hold the children (defaults to `\"children\"`)\n   */\n  constructor(\n    candidates?: Tree\u003cDataKey, ChildrenKey\u003e[],\n    dataKey: DataKey = \"data\",\n    childrenKey: ChildrenKey = \"children\"\n  )\n\n  /**\n   * The method to set an array of trees that are going to be filtered\n   *\n   * @param candidates An array of tree objects.\n   * @param dataKey The key of the object (and its children) which holds the data (defaults to `\"data\"`)\n   * @param childrenKey The key of the object (and its children) which hold the children (defaults to `\"children\"`)\n   */\n  setCandidates(\n    candidates: Tree\u003cDataKey, ChildrenKey\u003e[],\n    dataKey: DataKey = \"data\",\n    childrenKey: ChildrenKey = \"children\"\n  )\n\n  /**\n   * Filter the already set trees\n   *\n   * @param query A string query to match the dataKey of each candidate against.\n   * @param options Options\n   * @returns {Tree[]} An array of filtered trees. In a tree, the filtered data is at the last level (if it has\n   *   children, they are not included in the filtered tree)\n   */\n  filter(query: string, options: TreeFilterOptions = {}): Tree\u003cDataKey, ChildrenKey\u003e[]\n\n  /**\n   * The method to perform the filtering on the already set candidates\n   *\n   * @param query A string query to match the dataKey of each candidate against.\n   * @param options Options\n   * @returns {TreeFilterIndicesResult[]} An array candidate objects in form of `{data, index, parentIndices}` sorted by\n   *   best match against the query. Each object has the address of the object in the tree using `index` and `parent_indices`\n   */\n  filterIndices(query: string, options: TreeFilterOptions = {}): TreeFilterIndicesResult[]\n}\n```\n\n\u003c/details\u003e\n\u003cbr/\u003e\n\n**Example**:\n\n```js\nconst { TreeFilterer } = require(\"zadeh\")\n\nconst treeFilterer = new TreeFilterer()\n\nconst candidates = [\n  { data: \"bye1\", children: [{ data: \"hello\" }] },\n  { data: \"Bye2\", children: [{ data: \"_bye4\" }, { data: \"hel\" }] },\n  { data: \"eye\" },\n]\ntreeFilterer.setCandidates(candidates, \"data\", \"children\")\n```\n\n```ts\ntreeFilterer.filter(\"hel\")\n```\n\nreturns\n\n```ts\n;[\n  { data: \"Bye2\", children: [{ data: \"hel\" }] },\n  { data: \"bye1\", children: [{ data: \"hello\" }] },\n]\n```\n\n```ts\ntreeFilterer.filter(\"bye\")\n```\n\nreturns\n\n```ts\n;[\n  { data: \"bye1\", children: [] },\n  { data: \"Bye2\", children: [{ data: \"_bye4\" }] },\n  { data: \"Bye2\", children: [] },\n]\n```\n\n```ts\ntreeFilterer.filterIndices(\"bye\")\n```\n\nreturns\n\n```ts\n;[\n  { data: \"bye1\", index: 0, parent_indices: [] },\n  { data: \"_bye4\", index: 0, parent_indices: [1] },\n  { data: \"Bye2\", index: 1, parent_indices: [] },\n]\n```\n\n### score\n\n    score(string, query, options = {})\n\nScore the given string against the given query.\n\n- `string` - the string to score.\n- `query` - The query to score the string against.\n\n```js\nconst { score } = require('zadeh')\n\nscore('Me', 'me')    # 0.17099999999999999\nscore('Maybe', 'me') # 0.0693\n```\n\n### match\n\n    match(string, query, options = {})\n\nGives an array of indices at which the query matches the given string\n\n```js\nconst { match } = require(\"zadeh\")\n\nmatch(\"Hello World\", \"he\") // [0, 1]\nmatch(\"Hello World\", \"wor\") // [6, 7, 8]\nmatch(\"Hello World\", \"elwor\") // [1, 2, 6, 7, 8]\n```\n\n### wrap\n\n    wrap (string, query, options = {})\n\nGives an HTML/Markdown string that highlights the range for which the match happens\n\n```js\nwrap(\"helloworld\", \"he\")\n```\n\n\u003cstrong class=\"highlight\"\u003ehe\u003c/strong\u003elloworld\n\n```js\nwrap(\"Hello world\", \"he\")\n```\n\n\u003cstrong class=\"highlight\"\u003eHe\u003c/strong\u003ello world\n\n### options\n\nIn all the above functions, you can pass an optional object with the following keys\n\n```ts\n{\n    /** only for `filter` function */\n    /** The key to use when candidates is an object */\n    key?: T extends string ? never : keyof T\n\n    /** only for `filter` function */\n    maxResults?: number\n\n    /** @default false */\n    allowErrors?: boolean\n\n    /** @default true */\n    usePathScoring?: boolean\n\n    /** @default false */\n    useExtensionBonus?: boolean\n\n    pathSeparator?: '/' | '\\\\' | string\n}\n```\n\n## Deprecated functions\n\nThese deprecated functions are provided to support the API of `fuzzaldrin` and `fuzzaldrin-plus`.\nHowever, you should replace their usage with `StringArrayFilterer` or `ObjectArrayFilterer` classes that allow setting the candidates only once and perform filtering on those candidates multiple times. This is much more efficient than `filter` or `filterTree` functions.\n\n\u003cdetails\u003e\n\u003csummary\u003e `filter` function \u003c/summary\u003e\n\n### filter\n\n    filter(candidates, query, options = {})\n\nSort and filter the given candidates by matching them against the given query.\n\n- `candidates` - An array of strings or objects.\n- `query` - A string query to match each candidate against.\n- `options` - the options. You should provide a `key` in the options if an array of objects is passed.\n\nReturns an array of candidates sorted by best match against the query.\n\n```js\nconst { filter } = require(\"zadeh\")\n\n// With an array of strings\nlet candidates = [\"Call\", \"Me\", \"Maybe\"]\nlet results = filter(candidates, \"me\") // ['Me', 'Maybe']\n\n// With an array of objects\nconst candidates = [\n  { name: \"Call\", id: 1 },\n  { name: \"Me\", id: 2 },\n  { name: \"Maybe\", id: 3 },\n]\n\nresults = filter(candidates, \"me\", { key: \"name\" }) // [{name: 'Me', id: 2}, {name: 'Maybe', id: 3}]\n```\n\n**Deprecation Note**: use `StringArrayFilterer` or `ObjectArrayFilterer` class instead. `filter` internally uses this class, and in each call, it sets the candidates from scratch, which can slow down the process.\n\n\u003c/details\u003e\n\n# Comparison with other libraries\n\n### Zadeh vs Fuzzaldrin and Fuzzaldrin-plus\n\nAPI is backward compatible with Fuzzaldrin and Fuzzaldrin-plus. Additional functions are provided to achieve better performance that could suit your needs\n\nZadeh achieves 10x-20x performance improvement over Fuzzaldrin plus for chromium project with 300K files. This high performance is achieved using the following techniques.\n\n- Uses native C++ bindings that provide `~4x` performance benefit.\n- Use multiple threads to parallelize computation to achieve another `~4x` performance benefit.\n- Some miscellaneous improvements provide additional benefits.\n\nThis project potentially solves the following Atom fuzzy-finder issues if used.\nhttps://github.com/atom/fuzzy-finder/issues/271 and https://github.com/atom/fuzzy-finder/issues/88\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatom-community%2Fzadeh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatom-community%2Fzadeh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatom-community%2Fzadeh/lists"}