{"id":16817859,"url":"https://github.com/notherdev/notherlookup","last_synced_at":"2026-03-07T17:33:11.920Z","repository":{"id":13098008,"uuid":"15779428","full_name":"NOtherDev/NOtherLookup","owner":"NOtherDev","description":"Set of extensions for easy creation and manipulation of ILookups","archived":false,"fork":false,"pushed_at":"2014-01-18T21:59:19.000Z","size":1524,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-08T14:03:19.933Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","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/NOtherDev.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}},"created_at":"2014-01-09T21:04:18.000Z","updated_at":"2024-11-20T16:31:07.000Z","dependencies_parsed_at":"2022-08-23T14:10:08.070Z","dependency_job_id":null,"html_url":"https://github.com/NOtherDev/NOtherLookup","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NOtherDev%2FNOtherLookup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NOtherDev%2FNOtherLookup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NOtherDev%2FNOtherLookup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NOtherDev%2FNOtherLookup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NOtherDev","download_url":"https://codeload.github.com/NOtherDev/NOtherLookup/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248328640,"owners_count":21085360,"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-10-13T10:48:32.438Z","updated_at":"2026-03-07T17:33:11.866Z","avatar_url":"https://github.com/NOtherDev.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NOtherLookup\n\nNOtherLookup is a set of LINQ-like extensions that are aimed to make working with [the splendid `ILookup\u003cTKey, TValue\u003e`](http://notherdev.blogspot.com/2014/01/lookup-hidden-gem.html) even easier. It tries to resolve [two main `ILookup`'s deficiencies](http://notherdev.blogspot.com/2014/01/downsides-of-net-lookups.html) - lack of easy way to create it from the scratch and lack of easy way to apply transformations.\n\n## Table of Contents\n\n* [Creating `ILookup`](#creating-ilookup)\n  * [Empty `ILookup`](#empty-ilookup)\n  * [Creating `ILookup` manually - `Lookup.Builder`](#creating-ilookup-manually---lookupbuilder)\n  * [Converting `ILookup` from/to `IDictionary`](#converting-ilookup-fromto-idictionary)\n* [Manipulating single `ILookup`](#manipulating-single-ilookup)\n  * [`Select` - runs a projection on values for each key](#select---runs-a-projection-on-values-for-each-key)\n  * [`Where` - filters values for each key](#where---filters-values-for-each-key)\n  * [`OnEachKey` - runs arbitrary LINQ query on lookup elements (`IGrouping`s)](#oneachkey---runs-arbitrary-linq-query-on-lookup-elements-igroupings)\n* [Manipulating two `ILookup`s](#manipulating-two-ilookups)\n  * [`Concat` - concatenates values for each key](#concat---concatenates-values-for-each-key)\n  * [`Union` - gets the unique values for each key](#union---gets-the-unique-values-for-each-key)\n  * [`Except` - gets the difference of values set for each key](#except---gets-the-difference-of-values-set-for-each-key)\n  * [`Intersect` - gets the intersection of values set for each key](#intersect---gets-the-intersection-of-values-set-for-each-key)\n  * [`Join` - combines two lookups by values in each key using provided selector](#join---combines-two-lookups-by-values-in-each-key-using-provided-selector)\n  * [`Zip` - combines two lookups by pairs of values using provided selector for each key](#zip---combines-two-lookups-by-pairs-of-values-using-provided-selector-for-each-key)\n\n\n## Creating `ILookup`\n\n.NET's [`Lookup`](http://msdn.microsoft.com/en-us/library/bb460184\\(v=vs.110\\).aspx) class has no public constructor. NOtherLookup offers several ways to obtain an `ILookup\u003cTKey, TValue\u003e` instance.\n\n### Empty `ILookup`\n\nUseful to keep the code clean and obvious.\n\n    ILookup\u003cint, string\u003e emptyLookup = Lookup.Empty\u003cint, string\u003e();\n\n### Creating `ILookup` manually - `Lookup.Builder`\n\n    ILookup\u003cint, string\u003e lookup = Lookup.Builder\n        .WithKey(1, new[] { \"a\", \"b\" })\n        .WithKey(2, new[] { \"c\", \"d\" })\n        .Build();\n\nAllows specifying a custom key comparer:\n\n    ILookup\u003cint, string\u003e lookup = Lookup.Builder\n        .WithComparer(new CustomComparer())\n        .WithKey(1, new[] { \"a\", \"b\" })\n        .WithKey(2, new[] { \"c\", \"d\" })\n        .Build();\n\n### Converting `ILookup` from/to `IDictionary`\n\n    IDictionary\u003cint, string[]\u003e sourceDictionary = new Dictionary\u003cint, string[]\u003e()\n    {\n      { 1, new[] { \"a\", \"b\" }},\n      { 2, new[] { \"c\", \"d\" }}\n    };\n\nConverting dictionaries to lookups works for multiple types of `TValue` collections - `TValue[]`, `ICollection\u003cTValue\u003e` and `IList\u003cTValue\u003e` - as well as for `IEnumerable\u003cIGrouping\u003cTKey, TValue\u003e\u003e` (decomposed lookup).\n\n    ILookup\u003cint, string\u003e lookup = sourceDictionary.ToLookup();\n    \nAnd back to mutable `IDictionary` - doable using standard LINQ operators, but quite verbose and convoluted.\n    \n    Dictionary\u003cint, List\u003cstring\u003e\u003e backToDict = lookup.ToDictionary();\n\n## Manipulating single `ILookup`\n\nContrary to what we get using standard LINQ operators on `ILookup`, all the operators below maintains `ILookup` typing.\n\nLookup instance used in the examples:\n\n    ILookup\u003cint, string\u003e lookup = Lookup.Builder\n        .WithKey(1, new[] { \"a\", \"b\" })\n        .WithKey(2, new[] { \"c\", \"d\" })\n        .Build();\n        \n\n### `Select` - runs a projection on values for each key\n\n    ILookup\u003cint, string\u003e projected = lookup.Select(x =\u003e x + \"!\");\n    \nResult:\n\n    1 =\u003e [a!, b!]\n    2 =\u003e [c!, d!]\n    \n    \n### `Where` - filters values for each key\n\n    ILookup\u003cint, string\u003e filtered = lookup.Select(x =\u003e x != \"a\");\n    \nResult:\n\n    1 =\u003e [b]\n    2 =\u003e [c, d]\n    \n    \n### `OnEachKey` - runs arbitrary LINQ query on lookup elements (`IGrouping`s)\n\nIt is a generalization for any transformation that is supposed to run on each key in lookup. Note that `Select` and `Where` can be also easily accomplished through `OnEachKey` - they are standalone methods only for convenience.\n\n    ILookup\u003cint, string\u003e transformed = lookup.OnEachKey(g =\u003e g.Select(x =\u003e x + g.Key).Reverse());\n    \nResult:\n\n    1 =\u003e [b1, a1]\n    2 =\u003e [d2, c2]\n    \n\n## Manipulating two `ILookup`s\n\nContrary to what we get using standard LINQ operators on `ILookup`, all the operators below maintains `ILookup` typing.\n\nThe operators allow specifying custom key comparers, where applicable.\n\nLookup instances used in the examples:\n\n    ILookup\u003cint, string\u003e first = Lookup.Builder\n        .WithKey(1, new[] { \"a\", \"b\" })\n        .WithKey(2, new[] { \"c\", \"d\" })\n        .Build();\n        \n    ILookup\u003cint, string\u003e second = Lookup.Builder\n        .WithKey(1, new[] { \"a\", \"c\" })\n        .WithKey(3, new[] { \"e\", \"f\" })\n        .Build();\n\n\n### `Concat` - concatenates values for each key\n\n    ILookup\u003cint, string\u003e concatenated = first.Concat(second);\n    \nResult:\n\n    1 =\u003e [a, b, a, c]\n    2 =\u003e [c, d]\n    3 =\u003e [e, f]\n \n    \n### `Union` - gets the unique values for each key\n\n    ILookup\u003cint, string\u003e unionized = first.Union(second);\n    \nResult:\n\n    1 =\u003e [a, b, c]\n    2 =\u003e [c, d]\n    3 =\u003e [e, f]\n    \nAlso supports custom values comparers.\n\n\n### `Except` - gets the difference of values set for each key\n\n    ILookup\u003cint, string\u003e difference = first.Except(second);\n    \nResult:\n\n    1 =\u003e [b]\n    2 =\u003e [c, d]\n    \nAlso supports custom values comparers.\n\n\n### `Intersect` - gets the intersection of values set for each key\n\n    ILookup\u003cint, string\u003e intersection = first.Intersect(second);\n    \nResult:\n\n    1 =\u003e [a]\n    \nAlso supports custom values comparers.\n\n\n### `Join` - combines two lookups by values in each key using provided selector\n\n    ILookup\u003cint, string\u003e joined = first.Join(second, (x, y) =\u003e x + y);\n    \nResult:\n\n    1 =\u003e [aa, ac, ba, bc]\n    \n    \n### `Zip` - combines two lookups by pairs of values using provided selector for each key\n\n    ILookup\u003cint, string\u003e zipped = first.Zip(second, (x, y) =\u003e x + y);\n    \nResult:\n\n    1 =\u003e [aa, bc]\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotherdev%2Fnotherlookup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnotherdev%2Fnotherlookup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotherdev%2Fnotherlookup/lists"}