{"id":35216845,"url":"https://github.com/reynj/reynj","last_synced_at":"2026-04-09T05:32:11.526Z","repository":{"id":38105593,"uuid":"161824986","full_name":"reynj/reynj","owner":"reynj","description":".Net Library that aids in comparison and handling values ranges or time bounded periods.","archived":false,"fork":false,"pushed_at":"2026-03-04T15:37:22.000Z","size":445,"stargazers_count":16,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-04T16:42:12.650Z","etag":null,"topics":["csharp","date","dotnet","dotnet-standard","interval","mermaid","period","range","sequence","span","time"],"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/reynj.png","metadata":{"files":{"readme":".github/README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"reynj"}},"created_at":"2018-12-14T18:25:31.000Z","updated_at":"2026-03-17T13:24:54.000Z","dependencies_parsed_at":"2024-05-21T16:43:35.461Z","dependency_job_id":null,"html_url":"https://github.com/reynj/reynj","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/reynj/reynj","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reynj%2Freynj","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reynj%2Freynj/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reynj%2Freynj/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reynj%2Freynj/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reynj","download_url":"https://codeload.github.com/reynj/reynj/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reynj%2Freynj/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31587803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"online","status_checked_at":"2026-04-09T02:00:06.848Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["csharp","date","dotnet","dotnet-standard","interval","mermaid","period","range","sequence","span","time"],"created_at":"2025-12-29T22:32:40.576Z","updated_at":"2026-04-09T05:32:11.488Z","avatar_url":"https://github.com/reynj.png","language":"C#","readme":"﻿# Reynj\n\n[![Build](https://github.com/reynj/reynj/actions/workflows/build.yml/badge.svg)](https://github.com/reynj/reynj/actions/workflows/build.yml)\n[![NuGet](https://img.shields.io/nuget/v/reynj?logo=nuget\u0026logoSize=auto)](https://www.nuget.org/packages/Reynj)\n\n### What is Reynj?\n\n.Net Library that aids in comparison and handling value ranges or time bounded periods. Other names for a Range are Interval, Sequence, Period, Span, ... \n\nThis implementation is based on the [Range](https://martinfowler.com/eaaDev/Range.html \"Martin Fowler Range\") class as described by Martin Fowler.\n\nThe aim of this library is to provide a base Range class with all possible methods that can be performed on a Range, but also extension methods that can be used to handle and compare lists of Ranges.\nBelow is my list of features I want to implement, feel free to open an issue if something is missing on my list.\n\n\u003cdetails\u003e\n  \u003csummary\u003eClick to expand the list\u003c/summary\u003e\n\n    - [ ] Range\n      - [ ] Boundaries\n      - [ ] Implicit Index \u0026 Range support, see [Adding Index and Range support to existing library types](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/ranges#adding-index-and-range-support-to-existing-library-types)\n      - [ ] Type of Range should IComparable or IComparable\u003cT\u003e \n      - [x] Implements\n        - [x] IEquatable\n\t    - [x] IComparable\n\t    - [x] ICloneable\n        - [ ] IEnumerable ??\n      - [x] Operators\n         - [x] Equals\n\t     - [x] CompareTo\n\t     - [x] Convert to/from Tuple\n      - [ ] Methods\n        - [x] IsNullOrEmpty\n        - [x] Includes \u0026 IncludesAll\n\t    - [x] Overlaps\n\t    - [x] Touches\n        - [x] Gap\n        - [x] Merge (Union)\n\t    - [x] Split\n        - [x] Intersection\n\t    - [x] Exclusive\n\t    - [ ] EnumerateBy\n           - [x] Ascending (protected)\n           - [ ] Descending (protected)\n           - [ ] EnumerateBy without stepper function (via dynamic or Expressions) (public)\n\t    - [ ] Expand (change the end)\n        - [ ] Move (change the start and keep the gap between the end the same)\n        - [ ] CenterValue (get the value in the middle between start and end)\n      - [ ] Specific implemenations \n        - [ ] DateRange or Period\n           - [ ] EnumerateBy\n        - [ ] TimeRange\n           - [ ] EnumerateBy\n        - [ ] NumericRange\n           - [ ] EnumerateBy\n    - [ ] Collection of Ranges\n      - [ ] Methods\n        - [x] Lowest/Highest\n\t    - [x] IsContiguous\n        - [ ] ToRange (only possible for a Contiguous collection)\n        - [x] IsSingle\n\t    - [x] Reduce\n        - [x] Sort\n\t    - [x] Union\n\t    - [x] Intersect\n        - [x] Inverse\n\t    - [ ] Difference (Relative complement)\n\t    - [ ] Exclusive\n        - [ ] Enumerate (call EnumerateBy on all ranges)\n        - [ ] ContainsOverlaps (if ranges in the collection overlap)\n    - [ ] Serialize/Deserialize\n      - [x] SerializableAttribute \n      - [x] JsonConvertor (System.Text.Json)\n      - [x] JsonConvertor (Newtonsoft Json.NET)\n      - [ ] Entity Framework ValueConvertor\n      - [ ] NHibernate IUserType\n    - [x] Other\n      - [x] Range\u003cT\u003e.Empty and methods like Merge, Overlaps, Touches, ...\n      - [x] IsEmpty method vs Range\u003cT\u003e.Empty\n      - [x] Support for conversion between System.Range (C# 8.0) and Range\u003cint\u003e\n\u003c/details\u003e\n\n\n### Where can I get it?\n\nYou can install [Reynj with NuGet](https://www.nuget.org/packages/Reynj):\n\n    Install-Package Reynj\n    \nOr via the .NET Core command line interface:\n\n    dotnet add package Reynj\n\nEither commands, from Package Manager Console or .NET Core CLI, will download and install Reynj and all required dependencies.\n\n### How to use it?\n#### What is a Range?\nA Range is best visualized as a bar. It has a start and an end and contains everything between those two. Below is a visualization of Range of integers that start at 0 and end at 10. All whole numbers between 0 and 10 are included in the Range, except 10.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Range\u003cint\u003e\n    \n    Range[0,10]           : 2018-01-01.00, 10h\n```\n\nTo create this Range in code, you can do the following:\n\n```c#\nvar range = new Range\u003cint\u003e(0, 10);\n```\n\nThere is only one limitation, the start of Range must be lower or equal to the end.\n\n#### What are the types of Ranges that can be created?\nThe type of Range must derive from the [IComparable\u003cT\u003e](https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-2.2) interface. Below some common examples.\n  \n```c#\n// Numeric Ranges\nvar intRange = new Range\u003cint\u003e(0, 10);\nvar doubleRange = new Range\u003cdouble\u003e(0.0, 0.5);\n\n// Date and Time Ranges\nvar dateRange = new Range\u003cDateTime\u003e(new DateTime(2018, 12, 18), new DateTime(2018, 12, 25)); // Period\nvar timeSpanRange = new Range\u003cTimeSpan\u003e(TimeSpan.FromHours(0), TimeSpan.FromHours(6)); // Duration\n```\n\n#### What is an Empty Range?\nEvery Range where start and end are equal.\n\n```c#\nvar range1 = new Range\u003cint\u003e(10, 10);\nvar range2 = new Range\u003cint\u003e(0, 0);\nvar range3 = Range\u003cint\u003e.Empty;\n\n// Empty\nvar equals = range1.Equals(range2); // returns true\nvar equals = range2.Equals(range3); // returns true\nvar compare = range1.CompareTo(range3); // returns 0\n```\n\n#### What can be done with a Range?\n##### Determining equality\nBecause Range\u003cT\u003e implements the [IEquatable\u003cT\u003e](https://docs.microsoft.com/en-us/dotnet/api/system.iequatable-1?view=netcore-2.2) interface, including the operators the following can be done:\n  \n```c#\nvar range1 = new Range\u003cint\u003e(0, 10);\nvar range2 = new Range\u003cint\u003e(0, 10);\nvar range3 = new Range\u003cint\u003e(5, 9);\n\n// Equals\nvar res1 = range1.Equals(range2); // returns true\nvar res2 = range1.Equals(range3); // returns false\n\n// Equality Operators\nvar res3 = range1 == range2; // returns true\nvar res4 = range1 != range2; // returns false\nvar res5 = range1 == range3; // returns false\nvar res6 = range1 != range3; // returns true\n```\n\n##### Ordering or Sorting\nBecause Range\u003cT\u003e implements the [IComparable\u003cT\u003e](https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-2.2) interface, including the operators the following can be done:\n  \n```c#\nvar range1 = new Range\u003cint\u003e(0, 10);\nvar range2 = new Range\u003cint\u003e(5, 9);\n\n// CompareTo\nvar res1 = range1.CompareTo(range2); // returns -1\n\n// Equality Operators\nvar res2 = range1 \u003c range2;  // returns true\nvar res3 = range1 \u003e range2;  // returns false\nvar res4 = range1 \u003c= range2; // returns true\nvar res5 = range1 \u003e= range2; // returns false\n```\n\n##### Tuples\nA Range\u003cT\u003e has two primary properties, Start and End, because of that a Range\u003cT\u003e can also be represented as a Tuple, in the lastest versions of .Net as a [ValueTuple](https://docs.microsoft.com/en-us/dotnet/api/system.valuetuple?view=netcore-2.2).\nThe constructor of a Range\u003cT\u003e accepts a Tuple of two elements and there is an AsTuple method to convert a Range\u003cT\u003e to a ValueTuple\u003cT, T\u003e.\nConversion operators have been implemented to make this even more smoothly.\n\n```c#\nvar tuple = (0, 10);\nvar range = new Range\u003cint\u003e(tuple);\n\n// AsTuple\nvar otherTuple = range.AsTuple();\n\n// Conversion Operators\nvar otherTuple2 = range; // implicit from Range to Tuple\nvar otherRange = (Range\u003cint\u003e) tuple; // explicit from Tuple to Range\n```\n\n##### Methods\n###### Includes(T value), Includes(Range\u003cT\u003e range) and IncludesAll(IEnumerable\u003cT\u003e values)\nIncludes will return true if the given value is a part of the Range, otherwise false.\nIncludesAll will return true if all of the given values are part of the Range, otherwise false.\n\n```c#\nvar range = new Range\u003cint\u003e(0, 10);\n\n// Includes(T value)\nvar res1 = range1.Includes(5); // returns true\nvar res2 = range1.Includes(20); // returns false\n\n// Includes(Range\u003cT\u003e range)\nvar res1 = range1.Includes(new Range\u003cint\u003e(2, 7)); // returns true\nvar res2 = range1.Includes(new Range\u003cint\u003e(20, 30)); // returns false\n\n// IncludesAll\nvar res3 = range1.IncludesAll(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); // returns true\nvar res4 = range1.IncludesAll(0, 1, 2, 3, 4, 20, 6, 7, 8, 9); // returns false\n```\n\n###### Overlaps(Range\u003cT\u003e range)\nOverlaps will return true if two Ranges overlap. The following example are two overlapping ranges.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Overlaps\n    \n    Range[0,10]           : 2018-01-01.00, 10h\n    Range[5,15]           : active, 2018-01-01.05, 10h\n```\n\n```c#\nvar range1 = new Range\u003cint\u003e(0, 10);\nvar range2 = new Range\u003cint\u003e(5, 15);\nvar range3 = new Range\u003cint\u003e(15, 25);\n\n// Overlaps\nvar res1 = range1.Overlaps(range2); // returns true\nvar res2 = range2.Overlaps(range1); // returns true\n\nvar res3 = range1.Overlaps(range3); // returns false\n```\n\n###### Touches(Range\u003cT\u003e range)\nTouches will return true if two Ranges touch each other. The following example are two touching ranges.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Touches\n    \n    Range[0,5]        : 2018-01-01.00, 5h\n    Range[5,10]       : 2018-01-01.05, 5h\n```\n\n```c#\nvar range1 = new Range\u003cint\u003e(0, 10);\nvar range2 = new Range\u003cint\u003e(10, 20);\nvar range3 = new Range\u003cint\u003e(11, 20);\n\n// Overlaps\nvar res1 = range1.Touches(range2); // returns true\nvar res2 = range2.Touches(range1); // returns true\n\nvar res3 = range1.Touches(range3); // returns false\n```\n\n###### Gap(Range\u003cT\u003e range)\nGap returns a new Range that represents the gap between two Ranges.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Gap\n    \n    section Ranges\n    Range[0,5]         : 2018-01-01.00, 5h\n    Range[10,15]       : 2018-01-01.10, 5h\n\n    section Gap\n    Range[5,10]        : active, 2018-01-01.05, 5h\n```\n\n```c#\nvar range1 = new Range\u003cint\u003e(0, 10);\nvar range2 = new Range\u003cint\u003e(15, 20);\n\n// Gap\nvar gap1 = range1.Gap(range2); // returns new Range\u003cint\u003e(5, 10)\nvar gap2 = range2.Gap(range1); // returns new Range\u003cint\u003e(5, 10)\n```\n\n###### Merge(Range\u003cT\u003e range)\nMerge returns a new Range that represents the combined/merged Range, a [Logical disjunction](https://en.wikipedia.org/wiki/Logical_disjunction).\nAn exception is thrown when the ranges do not overlap or touch each other.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Merge\n    \n    section Ranges\n    Range[0,5]          : 2018-01-01.00, 5h\n    Range[5,20]         : 2018-01-01.05, 15h\n\n    section Merge\n    Range[0,20]         : active, 2018-01-01.00, 20h\n```\n\n```c#\nvar range1 = new Range\u003cint\u003e(0, 10);\nvar range2 = new Range\u003cint\u003e(5, 20);\n\n// Merge\nvar merge1 = range1.Merge(range2); // returns new Range\u003cint\u003e(0, 20)\nvar merge2 = range1 | range2; // returns new Range\u003cint\u003e(0, 20)\n```\n\n###### Split(Range\u003cT\u003e range)\nSplit returns a Tuple of two Ranges that have been split on the given value.\nAn exception is thrown when the value is not included in the Range.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Split\n    \n    section Range\n    Range[0,10]          : 2018-01-01.00, 10h\n    \n\n    section Split\n    Range[0,5]           : active, 2018-01-01.00, 5h\n    Range[5,10]          : active, 2018-01-01.05, 5h\n```\n\n```c#\nvar range = new Range\u003cint\u003e(0, 10);\n\n// Split\nvar split = range.Split(5); // returns (new Range\u003cint\u003e(0, 5), new Range\u003cint\u003e(5, 10))\n```\n\n###### Intersection(Range\u003cT\u003e range)\nIntersection returns a new Range that represents the intersection between the current Range and a given Range, a [Logical conjunction](https://en.wikipedia.org/wiki/Logical_conjunction).\nAn exception is thrown when the ranges do not overlap each other.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Intersection\n    \n    section Ranges\n    Range[0,10]         : 2018-01-01.00, 10h\n    Range[5,20]         : 2018-01-01.05, 15h\n\n    section Intersection\n    Range[5,10]         : active, 2018-01-01.05, 5h\n```\n\n```c#\nvar range1 = new Range\u003cint\u003e(0, 10);\nvar range2 = new Range\u003cint\u003e(5, 20);\n\n// Intersection\nvar intersection1 = range1.Intersection(range2); // returns new Range\u003cint\u003e(5, 10)\nvar intersection2 = range1 \u0026 range2; // returns new Range\u003cint\u003e(5, 10)\n```\n\n###### Exclusive(Range\u003cT\u003e range)\nExclusive returns a tuple of Ranges that that represent the parts they do not have in common, an [Exclusive or](https://en.wikipedia.org/wiki/Exclusive_or).\nAn exception is thrown when the ranges are null or Empty.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Exclusive\n    \n    section Ranges\n    Range[0,10]         : 2018-01-01.00, 10h\n    Range[5,20]         : 2018-01-01.05, 15h\n\n    section Exclusive\n    Range[0, 5]         : active, 2018-01-01.00, 5h\n    Range[10, 20]       : active, 2018-01-01.10, 10h\n```\n\n```c#\nvar range1 = new Range\u003cint\u003e(0, 10);\nvar range2 = new Range\u003cint\u003e(5, 20);\n\n// Exclusive\nvar exclusive = range1.Exlusive(range2); // returns (new Range\u003cint\u003e(0, 5), new Range\u003cint\u003e(10, 20))\n```\n\n###### IsEmpty()\nIsEmpty will return true if the start and the end of the Range are equal, otherwise false.\n\n```c#\nvar range1 = new Range\u003cint\u003e(0, 10);\nvar range2 = new Range\u003cint\u003e(10, 10);\n\n// IsEmpty\nvar res1 = range1.IsEmpty(); // returns false\nvar res2 = range2.IsEmpty(); // returns true\n```\n\n###### EnumerateBy(T step, Func\u003cT, T, T\u003e stepper) \u0026 EnumerateBy(TStep step, Func\u003cT, TStep, T\u003e stepper)\nEnumerateBy returns all values between start and end of the Range, given a step and stepper function. \nThe stepper function should always return a value higher than the previous call. There is an overload that allows the step to be of a different type than the typeof start and end of the Range.\nThis function is protected and should be used in class that inherits from Range\u003cT\u003e.\n\n```c#\n// EnumerateBy(T step, Func\u003cT, T, T\u003e stepper)\nvar range = new Range\u003cint\u003e(0, 10);\n\nvar values = range.EnumerateBy(2, (value, step) =\u003e value + step); // returns 0, 2, 4, 6, 8\n\n// EnumerateBy(TStep step, Func\u003cT, TStep, T\u003e stepper)\nvar startDate = new DateTimeOffset(2020, 8, 1, 8, 0, 0, TimeSpan.FromHours(2));\nvar endDate = new DateTimeOffset(2020, 8, 1, 14, 0, 0, TimeSpan.FromHours(2));\nvar range = new Range\u003cDateTimeOffset\u003e(startDate, endDate);\n\nvar values = range.EnumerateBy(TimeSpan.FromHours(2), (value, step) =\u003e value.Add(step)); // returns 2020-08-01 08:00, 2020-08-01 10:00, 2020-08-01 12:00\n```\n\n##### Extension Methods\n###### ToRange()\nWith the ToRange extension methods on both Range\u003cint\u003e and System.Range a conversion can be done between them.\nA System.Range and a Reynj.Range have not so much in common, a [System.Range](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#indices-and-ranges) is meant for accessing single elements or ranges in a sequence,\nbut by converting from them to a Range\u003cint\u003e it is possible to use all methods on Range\u003cint\u003e and Enumerable\u003cRange\u003cint\u003e\u003e.\n\n\n```c#\nvar range = new Range\u003cint\u003e(0, 10);\n\n// ToRange()\nvar sysRange = range.ToRange(); // returns new System.Range(0, 10)\n\n// ToRange()\nvar reynjRange = sysRange.ToRange(); // returns new Range\u003cint\u003e(0, 10)\n```\n\n##### Additional Libraries\n###### Reynj.Text.Json\nProvides a converter, named `RangeConverter` for the System.Text.Json library.\nBe aware that the type of Start and End also require a converter, either included in the System.Text.Json library or from another source.\n\n```c#\nvar options = new JsonSerializerOptions { Converters = { new RangeConverter() } }; // Required\n\n// Json Serialize\nvar range = new Range\u003cint\u003e(0, 10);\nvar jsonText = JsonSerializer.Serialize(range, options); // returns '{\"Start\":0,\"End\":10}'\n\n// Json Deserialize\nvar jsonRange = JsonSerializer.Deserialize(jsonRange, range.GetType(), options); // returns new Range\u003cint\u003e(0, 10)\n```\n\nIn your ASP.NET project you can add the following code to the Startup.cs to register the converter.\n\n```c#\nservices.AddControllers()\n    .AddJsonOptions(options =\u003e\n    {\n        options.JsonSerializerOptions.Converters.Add(new RangeConverter());\n        // ...\n    });\n```\n\n###### Reynj.Newtonsoft.Json\nProvides a converter, named `RangeConverter` for the Newtonsoft.Json library.\nBe aware that the type of Start and End also require a converter, either included in the Newtonsoft.Json library or from another source.\n\n```c#\nvar settings = new JsonSerializerSettings { Converters = { new RangeConverter() } }; // Required\n\n// Json Serialize\nvar range = new Range\u003cint\u003e(0, 10);\nvar jsonText = JsonConvert.SerializeObject(range, settings); // returns '{\"Start\":0,\"End\":10}'\n\n// Json Deserialize\nvar jsonRange = JsonConvert.DeserializeObject(json, typeOfRange, settings); // returns new Range\u003cint\u003e(0, 10)\n```\n\nIn your ASP.NET project you can add the following code to the Startup.cs to register the converter.\n\n```c#\nservices.AddControllers()\n    .AddNewtonsoftJson(options =\u003e\n    {\n        options.SerializerSettings.Converters.Add(new RangeConverter());\n        // ...\n    });\n```\n\n#### What is a Collection of Ranges?\nA Collection of Ranges is a group or list of Ranges of the same type. \n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title RangeCollection\n    \n    section Ranges\n    Range[0,5]         : 2018-01-01.00, 5h\n    Range[7,10]        : 2018-01-01.07, 3h\n    Range[10,15]       : 2018-01-01.10, 5h\n    Range[18,25]       : 2018-01-01.18, 7h\n```\n\nTo create an IEnumerable\u003cRange\u003cT\u003e\u003e in code, you can do the following:\n\n```c#\n// Collection based on an IEnumerable\u003cRange\u003cT\u003e\u003e\nvar ranges = new List\u003cRange\u003cint\u003e\u003e() {\n    new Range\u003cint\u003e(0, 10),\n    new Range\u003cint\u003e(10, 20)\n}\n```\n\n#### What can be done with a Collection of Ranges?\n\n##### Extension Methods\n###### Lowest() / Highest()\nThey return the lowest start or highest end of the all the Ranges in the collection.\n\n```c#\nvar ranges = new List\u003cRange\u003cint\u003e\u003e\n{\n    new Range\u003cint\u003e(0, 10),\n    new Range\u003cint\u003e(10, 20)\n};\n\n// Lowest\nvar lowest = ranges.Lowest(); // returns 0\n\n// Highest\nvar highest = ranges.Highest(); // returns 20\n```\n\n###### Reduce()\nReturns a new Collection of Ranges where all overlapping and touching Ranges have been merged and empty Ranges have been removed.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Reduce\n    \n    section Ranges\n    Range[0,5]         : 2018-01-01.00, 5h\n    Range[3,10]        : 2018-01-01.03, 7h\n    Range[10,15]       : 2018-01-01.10, 5h\n    Range[18,25]       : 2018-01-01.18, 7h\n\n    section Reduced\n    Range[0,15]        : active, 2018-01-01.00, 15h\n    Range[18,25]       : active, 2018-01-01.18, 7h\n```\n\n```c#\nvar ranges = new[]\n{\n    new Range\u003cint\u003e(0, 5),\n    new Range\u003cint\u003e(3, 10),\n    new Range\u003cint\u003e(10, 15),\n    new Range\u003cint\u003e(18, 25)\n});\n\n// Reduce\nvar reduced = ranges.Reduce(); // returns new[] { new Range\u003cint\u003e(0, 15), new Range\u003cint\u003e(18, 25) }\n```\n\n###### Union()\nReturns the [union](https://en.wikipedia.org/wiki/Union_(set_theory)) of two Collections of Ranges while reducing them.\nAn exception is thrown when one or both of the ranges are null.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Union\n    \n    section Ranges 1\n    Range[0,5]         : 2018-01-01.00, 5h\n    Range[3,10]        : 2018-01-01.03, 7h\n    Range[10,15]       : 2018-01-01.10, 5h\n\n\n    section Ranges 2\n    Range[15,17]       : 2018-01-01.15, 2h\n    Range[18,25]       : 2018-01-01.18, 7h\n\n    section Unioned\n    Range[0,17]        : active, 2018-01-01.00, 17h\n    Range[18,25]       : active, 2018-01-01.18, 7h\n```\n\n```c#\nvar ranges1 = new[]\n{\n    new Range\u003cint\u003e(0, 5),\n    new Range\u003cint\u003e(3, 10),\n    new Range\u003cint\u003e(10, 15)\n});\n\nvar ranges2 = new[]\n{\n    new Range\u003cint\u003e(15, 17),\n    new Range\u003cint\u003e(18, 25)\n});\n\n// Union\nvar unioned = ranges1.Union(ranges2); // returns new[] { new Range\u003cint\u003e(0, 17), new Range\u003cint\u003e(18, 25) }\n```\n\n###### Intersect()\nReturns the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory)) of two Collections of Ranges while reducing them.\nAn exception is thrown when one or both of the ranges are null.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Intersect\n    \n    section Ranges 1\n    Range[0,5]           : 2018-01-01.00, 5h\n    Range[3,10]          : 2018-01-01.03, 7h\n    Range[10,15]         : 2018-01-01.10, 5h\n    Range[18,20]         : 2018-01-01.18, 2h\n\n    section Ranges 2\n    Range[1,8]           : 2018-01-01.01, 7h\n    Range[12,25]         : 2018-01-01.12, 13h\n\n    section Intersection\n    Range[1,8]           : active, 2018-01-01.01, 7h\n    Range[12,15]         : active, 2018-01-01.12, 3h\n    Range[18,20]         : active, 2018-01-01.18, 2h\n```\n\n```c#\nvar ranges1 = new[]\n{\n    new Range\u003cint\u003e(0, 5),\n    new Range\u003cint\u003e(3, 10),\n    new Range\u003cint\u003e(10, 15),\n    new Range\u003cint\u003e(18, 20)\n});\n\nvar ranges2 = new[]\n{\n    new Range\u003cint\u003e(1, 8),\n    new Range\u003cint\u003e(12, 25)\n});\n\n// Intersect\nvar intersection = ranges1.Intersect(ranges2); // returns new[] { new Range\u003cint\u003e(1, 8), new Range\u003cint\u003e(12, 15), new Range\u003cint\u003e(18, 20) }\n```\n\n\u003c!--\n###### Difference\nReturns the set difference or [relative complement](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement) of two Collections of Ranges while reducing them.\nAn exception is thrown when one or both of the ranges are null.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Difference\n    \n    section Ranges 1\n    Range[0,5]           : 2018-01-01.00, 5h\n    Range[3,10]          : 2018-01-01.03, 7h\n    Range[10,15]         : 2018-01-01.10, 5h\n    Range[18,20]         : 2018-01-01.18, 2h\n\n    section Ranges 2\n    Range[1,8]           : 2018-01-01.01, 7h\n    Range[12,25]         : 2018-01-01.12, 13h\n\n    section DifferenceOf\n    Range[15,18]         : active, 2018-01-01.15, 3h\n    Range[20,25]         : active, 2018-01-01.20, 5h\n```\n\n```c#\nvar ranges1 = new[]\n{\n    new Range\u003cint\u003e(0, 5),\n    new Range\u003cint\u003e(3, 10),\n    new Range\u003cint\u003e(10, 15),\n    new Range\u003cint\u003e(18, 20)\n});\n\nvar ranges2 = new[]\n{\n    new Range\u003cint\u003e(1, 8),\n    new Range\u003cint\u003e(12, 25)\n});\n\n// Set Difference\nvar differenceOf = ranges1.Difference(ranges2); // returns new[] { new Range\u003cint\u003e(15, 18), new Range\u003cint\u003e(20, 25) }\n```\n\n###### Exclusive()\nReturns the [exclusive or](https://en.wikipedia.org/wiki/Symmetric_difference) of two Collections of Ranges while reducing them.\nAn exception is thrown when one or both of the ranges are null.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Exclusive\n    \n    section Ranges 1\n    Range[0,5]           : 2018-01-01.00, 5h\n    Range[3,10]          : 2018-01-01.03, 7h\n    Range[10,15]         : 2018-01-01.10, 5h\n    Range[18,20]         : 2018-01-01.18, 2h\n\n    section Ranges 2\n    Range[1,8]           : 2018-01-01.01, 7h\n    Range[12,25]         : 2018-01-01.12, 13h\n\n    section ExclusiveOf\n    Range[0,1]           : active, 2018-01-01.00, 1h\n    Range[8,12]          : active, 2018-01-01.08, 4h\n    Range[15,18]         : active, 2018-01-01.15, 3h\n    Range[20,25]         : active, 2018-01-01.20, 5h\n```\n\n```c#\nvar ranges1 = new[]\n{\n    new Range\u003cint\u003e(0, 5),\n    new Range\u003cint\u003e(3, 10),\n    new Range\u003cint\u003e(10, 15),\n    new Range\u003cint\u003e(18, 20)\n});\n\nvar ranges2 = new[]\n{\n    new Range\u003cint\u003e(1, 8),\n    new Range\u003cint\u003e(12, 25)\n});\n\n// Intersect\nvar exclusiveOr = ranges1.Exclusive(ranges2); // returns new[] { new Range\u003cint\u003e(0, 1), new Range\u003cint\u003e(8, 12), new Range\u003cint\u003e(15, 18), new Range\u003cint\u003e(20, 25) }\n```\n--\u003e\n\n###### Inverse()\nReturns a new Collection of Ranges that is the inversion of the Ranges. Meaning all gaps between the ranges are returned including the gap between the minvalue and the first start and the last end and the maxvalue.\nAlso known as the [absolute complement](https://en.wikipedia.org/wiki/Complement_(set_theory)#Absolute_complement).\nAn exception is thrown when the type of Range has no MinValue and MaxValue or when they are not passed.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title Inverse\n    \n    section Ranges\n    Range[0,5]           : 2018-01-01.00, 5h\n    Range[10,15]         : 2018-01-01.10, 5h\n    Range[18,20]         : 2018-01-01.18, 2h\n\n    section Inversion\n    Range[-∞, 0]         : active, 2017-12-31.21, 3h\n    Range[5,10]          : active, 2018-01-01.05, 5h\n    Range[15,28]         : active, 2018-01-01.15, 3h\n    Range[20,+∞]         : active, 2018-01-01.20, 3h\n```\n\n```c#\nvar ranges = new[]\n{\n    new Range\u003cint\u003e(0, 5),\n    new Range\u003cint\u003e(10, 15),\n    new Range\u003cint\u003e(18, 20)\n});\n\n// Inverse\nvar inversion = ranges.Inverse(); // returns new[] { new Range\u003cint\u003e(int.MinValue, 0), new Range\u003cint\u003e(5, 10), new Range\u003cint\u003e(15, 18), new Range\u003cint\u003e(20, int.MaxValue) }\n```\n\n###### IsContiguous()\nCheck if a collection of Ranges only contains touching Ranges and form a contiguous sequence.\n\n```mermaid\ngantt\n    dateFormat  YYYY-MM-DD.HH\n    axisFormat %-H\n    title IsContiguous\n    \n    section Is Contiguous\n    Range[3,10]          : 2018-01-01.03, 7h\n    Range[10,15]         : 2018-01-01.10, 5h\n\n    section Is Contiguous (overlap)\n    Range[0, 5]          : crit, done, 2018-01-01.00, 5h\n    Range[3,10]          : crit, done, 2018-01-01.03, 7h\n    Range[10,15]         : active, 2018-01-01.10, 5h\n\n    section Is Contiguous (gap)\n    Range[3,10]          : active, done, 2018-01-01.03, 7h\n    Range[10,15]         : crit, done, 2018-01-01.10, 5h\n    Range[18,25]         : crit, done, 2018-01-01.18, 7h\n```\n\n```c#\nvar contiguousRanges = new[]\n{\n    new Range\u003cint\u003e(10, 15),\n    new Range\u003cint\u003e(18, 25)\n});\n\nvar withOverlapRanges = new[]\n{\n    new Range\u003cint\u003e(0, 5),\n    new Range\u003cint\u003e(3, 10),\n    new Range\u003cint\u003e(10, 15)\n});\n\nvar notTouchingRanges = new[]\n{\n    new Range\u003cint\u003e(3, 10),\n    new Range\u003cint\u003e(10, 15),\n    new Range\u003cint\u003e(18, 25)\n});\n\n// IsContiguous\nvar isContiguous1 = contiguousRanges.IsContiguous(); // returns true\nvar isContiguous2 = withOverlapRanges.IsContiguous(); // returns false\nvar isContiguous3 = notTouchingRanges.IsContiguous(); // returns false\n```\n\n###### IsSingle()\nHelper method on IEnumerable that returns true if the sequence contains exacly one element.\n\n```c#\nvar emptyList = new List\u003cRange\u003cint\u003e\u003e();\n\nvar singleItemList = new[]\n{\n    new Range\u003cint\u003e(0, 5)\n});\n\nvar multiItemList = new[]\n{\n    new Range\u003cint\u003e(3, 10),\n    new Range\u003cint\u003e(10, 15)\n});\n\n// IsSingle\nvar isSingle1 = emptyList.IsSingle(); // returns false\nvar isSingle2 = singleItemList.IsSingle(); // returns true\nvar isSingle3 = multiItemList.IsSingle(); // returns false\n```","funding_links":["https://github.com/sponsors/reynj"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freynj%2Freynj","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freynj%2Freynj","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freynj%2Freynj/lists"}