DotNetExtensionLib 4.0.0
dotnet add package DotNetExtensionLib --version 4.0.0
NuGet\Install-Package DotNetExtensionLib -Version 4.0.0
<PackageReference Include="DotNetExtensionLib" Version="4.0.0" />
<PackageVersion Include="DotNetExtensionLib" Version="4.0.0" />
<PackageReference Include="DotNetExtensionLib" />
paket add DotNetExtensionLib --version 4.0.0
#r "nuget: DotNetExtensionLib, 4.0.0"
#:package DotNetExtensionLib@4.0.0
#addin nuget:?package=DotNetExtensionLib&version=4.0.0
#tool nuget:?package=DotNetExtensionLib&version=4.0.0
DotNetExtensionLib
This is a .NET8 / .NET9 helper library containing a wide range of helper classes and extension methods to fill the gaps left by the framework. For older versions of the framework, use the older version of the package.
Features
Extensions
Collections
Insert in ReadOnlyCollection
/// <summary>
/// Creates a new readonly collection containing the source and an extra item inserted in it.
/// </summary>
/// <typeparam name="T">The item type.</typeparam>
/// <param name="source">The source items.</param>
/// <param name="index">The index where the extra item is inserted.</param>
/// <param name="item">The extra item to insert.</param>
/// <returns>A new readonly collection.</returns>
public static IReadOnlyCollection<T> Insert<T>(this IReadOnlyCollection<T> source, int index, T item)
Partition a ReadOnlyCollection based on a predicate
/// <summary>
/// Partition a collection into two memory areas, the first one containing the items matching the predicate, the second one containing the other items.
/// </summary>
/// <typeparam name="T">The item type.</typeparam>
/// <param name="items">The items to separate.</param>
/// <param name="predicate">The predicate used to separate the items.</param>
/// <returns>Two memory areas, the first one containing the items matching the predicate, the second one containing the other items.</returns>
/// <remarks>This method is 33% faster than Split, but Split does not require a collection of items as an input and works on IEnumerable.</remarks>
public static (Memory<T> Matches, Memory<T> Others) Partition<T>(this IReadOnlyCollection<T> items, Func<T, bool> predicate)
Dictionary
Get a value from a IDictionary or call a factory if the key does not exist
/// <summary>
/// Gets the value matching the key in the dictionary, or the default value if the key is not present.
/// </summary>
/// <typeparam name="TK">The key type.</typeparam>
/// <typeparam name="TV">The value type.</typeparam>
/// <param name="dictionary">The dictionary.</param>
/// <param name="key">The key searched in the dictionary.</param>
/// <param name="defaultValueFactory">The factory for the default value to return if the key is not present in the dictionary.</param>
/// <returns>The value indexed by the key, or the default value if the key doesn't exist in the dictionary.</returns>
/// <remarks>The extension method with a constant value instead of a factory exists in System.Collections.Generic</remarks>
public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dictionary, TK key, Func<TV> defaultValueFactory)
Please note that GetValueOrDefault already exists in the framework but not with a lazy evaluation (factory).
Get a value from a IDictionary or call a factory to add it if the key does not exist
/// <summary>
/// Gets the value matching the key in the dictionary, or adds a new value for this key if the key is not present and returns it.
/// </summary>
/// <typeparam name="TK">The key type.</typeparam>
/// <typeparam name="TV">The value type.</typeparam>
/// <param name="dictionary">The dictionary.</param>
/// <param name="key">The key searched in the dictionary.</param>
/// <param name="valueFactory">The factory for the new value to return if the key is not present in the dictionary.</param>
/// <returns>The value indexed by the key, or the new value if the key doesn't exist in the dictionary.</returns>
public static TV GetOrAdd<TK, TV>(this IDictionary<TK, TV> dictionary, TK key, Func<TV> valueFactory)
IEnumerable
Convert an IEnumerable into a list with an estimated size to avoid unneccessary allocations and copies
/// <summary>
/// Creates a List<T> from an IEnumerable<T> with an estimated capacity to avoid
/// growing the list and performing multiple allocations and copies.
/// </summary>
/// <typeparam name="T">The item type.</typeparam>
/// <param name="items">The items.</param>
/// <param name="capacity">The estimated capacity.</param>
/// <returns>A new list containing the items.</returns>
/// <remarks>This is only useful when the framework has no clue of the actual size (dynamic data). Even Enumerable.Range returns a range with a known size.</remarks>
/// <remarks>Benchmark with 20 items:
/// | Method | Mean | Error | StdDev | Allocated |
/// |--------------------- |---------:|---------:|---------:|----------:|
/// | Enumerable_ToList | 89.35 ns | 10.28 ns | 0.564 ns | 400 B |
/// | ExtEnumerable_ToList | 56.40 ns | 11.08 ns | 0.607 ns | 168 B |
/// </remarks>
public static List<T> ToList<T>(this IEnumerable<T> items, int capacity)
Virtually remove items for an IEnumerable by keeping track of skipped items indices.
/// <summary>
/// Skips items in an enumeration based on their index and returns a dictionary of (index, item) containing the unskipped items and their index.
/// </summary>
/// <typeparam name="T">The item type.</typeparam>
/// <param name="items">The items.</param>
/// <param name="indices">The indices to skip.</param>
/// <returns>A dictionary of(index, item) </returns>
public static Dictionary<int, T> SkipIndices<T>(this IEnumerable<T> items, IReadOnlyCollection<int> indices)
Find the index of an item matching a predicate in an IEnumerable
/// <summary>
/// Returns the index of the first item of the source that matches a predicate.
/// </summary>
/// <typeparam name="T">The item type.</typeparam>
/// <param name="items">The items source.</param>
/// <param name="predicate">The predicate.</param>
/// <returns>The index of the first item of the source that matches a predicate.</returns>
/// <remarks>Benchmark with 10000 items (linq equivalent is: items.Index().FirstOrDefault(predicate).Index)
/// | Method | Mean | Error | StdDev | Allocated |
/// |---------------------------- |-----------:|---------:|---------:|----------:|
/// | Linq_Equivalent_FindIndexOf | 7,466.6 ns | 13.43 ns | 11.90 ns | 104 B |
/// | ExtEnumerable_FindIndexOf | 772.6 ns | 1.47 ns | 1.30 ns | 40 B |
/// </remarks>
public static int IndexOf<T>(this IEnumerable<T> items, Func<T, bool> predicate)
Find an item matching a predicate in an IEnumerable, returning true if it is found, and false otherwise.
/// <summary>
/// Returns a value indicating whether an item of the source matches the predicate, and if true, returns the item as an out value.
/// </summary>
/// <typeparam name="T">The item type.</typeparam>
/// <param name="items">The items source.</param>
/// <param name="predicate">The predicate.</param>
/// <returns>True if an item of the source matches the predicate, false otherwise.</returns>
public static bool TryFind<T>(this IEnumerable<T> items, Func<T, bool> predicate, out T value)
Splits an IEnumerable based of a predicate.
/// <summary>
/// Splits an enumeration into two lists, the first one containing the items matching the predicate, the second one containing the other items.
/// </summary>
/// <typeparam name="T">The item type.</typeparam>
/// <param name="items">The items to separate.</param>
/// <param name="predicate">The predicate used to separate the items.</param>
/// <returns>Two lists, the first one containing the items matching the predicate, the second one containing the other items.</returns>
/// <remarks>This method is 33% slower than Partition, but Partition requires a collection of items.</remarks>
public static (List<T> Matches, List<T> Others) Split<T>(this IEnumerable<T> items, Func<T, bool> predicate)
List
Remove the first item of a List matching a predicate.
public static bool Remove<T>(this List<T> source, Predicate<T> predicate)
public static bool Remove<T>(this IList<T> source, Func<T, bool> predicate)
Since arrays implement IList, calling Remove on an array can result into a runtime exception.
DataRow
Return a string column value.
public static string GetString(this DataRow row, string columnName)
Parse an int column value
public static bool TryParseInt(this DataRow row, string columnName, out int value)
public static bool TryParseInt(this DataRow row, string columnName, NumberStyles numberStyles, CultureInfo cultureInfo, out int value)
public static int ParseInt(this DataRow row, string columnName, CultureInfo cultureInfo = null)
Math helper
public enum ComparisonResult
{
Equal = 0,
TooBig = -1,
TooSmall = 1
}
/// <summary>
/// Performs a dichotomic search of a value between 0 and 1 based on a predicate, in a maximum number of steps.
/// If the number of steps is reached, returns the lowest value found.
/// </summary>
/// <param name="steps">The maximum number of steps before stopping the search.</param>
/// <param name="predicate">The predicate.</param>
/// <returns>The best value found.</returns>
/// <remarks>See also List.BinarySearch if you are looking for a value in a sorted list.</remarks>
public static decimal DichotomicSearch(int steps, Func<decimal, ComparisonResult> predicate)
This method is useful to quickly find a value that suits our needs, for instance if one is trying to compute the radius of a circle that intersects something, one could do:
var bestRadiusFound = MAX_RADIUS * DichotomicSearch(10, r => Insersect(MAX_RADIUS * r) ? ComparisonResult.TooBig : ComparisonResult.TooSmall);
Text manipulation
CsvHelper
Escape the quotes inside a text, and put the whole text between quotes
/// <summary>
/// Returns a given text between quotes and escapes any existing quote by duplicating the quote.
/// </summary>
/// <param name="text">The original text.</param>
/// <returns>The quoted text with any inner quote being escaped.</returns>
public static string EscapeAndQuote(string text)
SplitStringHelper
Split a CSV line
/// <summary>
/// Parses a text line that uses delimiters and text qualifiers (typically a CSV line) and returns the records it contains.
/// </summary>
/// <param name="source">The text line.</param>
/// <param name="fieldDelimiter">The character used to separate the records.</param>
/// <param name="textQualifier">The character used to enclose records containing the same character as the field delimiter so that they are not interpreted as delimiters.</param>
/// <returns>The list of records contained in the line, or null if the text qualifiers are mismatched (not always in pairs)
/// or used within a non-quoted field, or if the character following a closing quote is not the field delimiter.</returns>
public static List<string> ParseSeparatedValues(ReadOnlySpan<char> source, char fieldDelimiter, char textQualifier)
Regroup items that should be one and only one entry
/// <summary>
/// Takes a sequence of items and regroups those that are withing an item starting with a given opening delimiter and and an item ending with a given closing delimiter.
/// </summary>
/// <param name="sourceItems">The source items.</param>
/// <param name="delimiter">The delimiter that will be inserted between the items of a same group.</param>
/// <param name="openingDelimiter">The opening delimiter.</param>
/// <param name="closingDelimiter">The closing delimiter.</param>
/// <param name="removeOpeningClosingDelimiters">Indicates whether the opening and closing delimiters must be removed from the regrouped items.</param>
/// <returns>A sequence of items, some of which have been regrouped together.</returns>
/// <example>If delimiter = ' ', opening delimiter is '[' and closing delimiter is ']', the sequence
/// { "Entering", "[Guru", "Meditation]", "Now" } will return
/// { "Entering", "[Guru Meditation]", "Now" } if removeOpeningClosingDelimiters is false or
/// { "Entering", "Guru Meditation", "Now" } if removeOpeningClosingDelimiters is true
/// </example>
TextFileEncodingDetector
Detect the encoding of a text file
Taken from https://gist.github.com/TaoK/945127
public static Encoding DetectTextFileEncoding(string inputFilename)
public static Encoding DetectTextFileEncoding(FileStream inputFileStream)
public static Encoding DetectTextFileEncoding(FileStream inputFileStream, long heuristicSampleSize, out bool hasBom)
public static Encoding DetectTextByteArrayEncoding(byte[] textData)
public static Encoding DetectTextByteArrayEncoding(byte[] textData, out bool hasBom)
public static string GetStringFromByteArray(byte[] textData, Encoding defaultEncoding)
public static string GetStringFromByteArray(byte[] textData, Encoding defaultEncoding, long maxHeuristicSampleSize)
public static Encoding DetectBOMBytes(byte[] bomBytes)
public static Encoding DetectUnicodeInByteSampleByHeuristics(byte[] SampleBytes)
TextHelper
Remove characters from a text
/// <summary>
/// Removes all characters from the text that match the predicate.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="predicate">The predicate to match the characters to remove.</param>
/// <returns>The text without any characters matching the predicate.</returns>
public static string FastRemove(ReadOnlySpan<char> text, Func<char, bool> predicate)
The following methods are the fastest if the list of characters is constant.
/// <summary>
/// Removes all the given characters from the text.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="c1">A character to remove from the text.</param>
/// <param name="c2">A character to remove from the text.</param>
/// <returns>The text without any of the given character.</returns>
/// <remarks>To remove only 1 character, string.Replace is faster.</remarks>
public static string FastRemove(ReadOnlySpan<char> text, char c1, char c2)
...
/// <summary>
/// Removes all the given characters from the text.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="c1">A character to remove from the text.</param>
/// <param name="c2">A character to remove from the text.</param>
/// <param name="c3">A character to remove from the text.</param>
/// <param name="c4">A character to remove from the text.</param>
/// <param name="c5">A character to remove from the text.</param>
/// <param name="c6">A character to remove from the text.</param>
/// <param name="c7">A character to remove from the text.</param>
/// <param name="c8">A character to remove from the text.</param>
/// <param name="c9">A character to remove from the text.</param>
/// <returns>The text without any of the given character.</returns>
/// <remarks>To remove only 1 character, string.Replace is faster.</remarks>
public static string FastRemove(ReadOnlySpan<char> text, char c1, char c2, char c3, char c4, char c5, char c6, char c7, char c8, char c9)
Remove diacritics from a text
/// <summary>
/// Remove diacritics from a text.
/// </summary>
/// <param name="text">The text.</param>
/// <returns>The text without diacritics</returns>
public static string RemoveDiacritics(this string text)
StringBuilder conditional append line
public static StringBuilder AppendLineIf(this StringBuilder sb, bool condition, string text)
Get Levenshtein distance
Taken from https://github.com/DanHarltey/Fastenshtein, but with a different input type.
/// <summary>
/// Returns the Levenshtein distance between two string represented as spans.
/// </summary>
/// <param name="s">The first string</param>
/// <param name="t">The seconde string</param>
/// <returns>The Levenshtein distance between the first string and the second string.</returns>
/// <remarks>For better performance on strings, use the Fastenshtein package.
/// https://github.com/DanHarltey/Fastenshtein
/// This implementation is a copy of the Fastenshtein one, but with ReadOnlySpan parameters (and similar performances).
/// </remarks>
public static int GetLevenshteinDistance(ReadOnlySpan<char> s, ReadOnlySpan<char> t)
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
4.0.0
* Target .NET8 and .NET9 only. Use previous versions for older frameworks.
* Removed obsolete method IReadOnlyCollection.Contains<T>(T item)
* Removed obsolete method IReadOnlyDictionary.ValueOrDefault<TK, TV>(TK key, TV defaultValue = default)
* Renamed method IDictionary.ValueOrDefault<TK, TV>(TK key, Func<TV> defaultValueFactory) to GetValueOrDefault to match the .NET method
* Renamed IEnumerable.FindIndexOf<T>(Func<T, bool> predicate) to IndexOf
* Removed IEnumerator.ToEnumerable() because it generates a state-based machine. Replace it with a while (iterator.MoveNext()) loop (which can be used to fill a concrete type such as List)
* Added culture overloads to DataRow.TryParseInt
* Improved ParseSeparatedValues performances by 33%. The method now takes ReadOnlySpan<char> as input and rejects some badly formed csv.
* Changed Levenshtein's implementation of GetLevenshteinDistance by the one from Fastenshtein while still taking ReadOnlySpan<char> as parameters.
* Added IReadOnlyCollection.Partition that is 33% faster than Split and used less memory
* Added Remove<T>(Predicate<T> predicate) method to List<T> and IList<T>
* MemberwiseComparer now implements IEqualityComparer instead of IComparer
* RegroupItems now takes a IEnumerable<string> parameter instead of ICollection<string>
* Removed TextHelper.StripPunctation that was very slow (use FastRemove instead)
* Removed TextHelper.TrimPunctuation that was very slow
* Removed TextHelper.LastChar that is now easily written as text[^1]
* Added TextHelper.FastRemove(ReadOnlySpan<char> text, Func<char, bool> predicate) that is slow
* Added TextHelper.FastRemove(ReadOnlySpan<char> text, char c1, char c2) with overloads up to 9 characters that is very fast
* Removed TextHelper.IsDigitOrX
3.0.0
* Target .NET Standard 2.0
2.0.0
* .NET 6 as the only target
* Removed VerboseDictionary as Dictionary<,> now throws exceptions containing the key.
1.4.0
* Added the VerboseDictionary class.
* Added .NET Core 3.1 and .NET 5 to the targets
1.3.0
* Changed ValueOrDefault to be an extension method of IReadOnlyDictionary so it can work with Dictionary as before but also custom dictionaries implementing IReadOnlyDictionary.
* Added Split extension method to IEnumerable.
* Removed .NET Framework 4.5.2 and 4.6.2 from the targets.
* Added .NET Framework 4.8 to the targets
1.2.0
* Added GetOrAdd extension method to the IDictionary.
1.1.0
* Replaced RegroupItems with ParseSeparatedValues. Added documentation.
1.0.1
* Added .NET Framework 4.5.2, 4.6.2, and 4.7.2 to the targets
1.0.0
* Initial Revision