Raccoon.Ninja.Tools 1.4.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Raccoon.Ninja.Tools --version 1.4.0                
NuGet\Install-Package Raccoon.Ninja.Tools -Version 1.4.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Raccoon.Ninja.Tools" Version="1.4.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Raccoon.Ninja.Tools --version 1.4.0                
#r "nuget: Raccoon.Ninja.Tools, 1.4.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Raccoon.Ninja.Tools as a Cake Addin
#addin nuget:?package=Raccoon.Ninja.Tools&version=1.4.0

// Install Raccoon.Ninja.Tools as a Cake Tool
#tool nuget:?package=Raccoon.Ninja.Tools&version=1.4.0                

Racoon Ninja Tools

Publish GitHub Release NuGet Version NuGet Downloads GitHub last commit


Quality Gate Status Reliability Rating Maintainability Rating Technical Debt Vulnerabilities Bugs Code Smells Duplicated Lines (%) Coverage Lines of Code


Description

This is a collection of helpers and tools I find useful enough to reuse in multiple projects. I hope this can help other people too. 😃

The idea of the package is to be lightweight and without external dependencies as much as possible. Right now, the project is fully standalone.

Changelog

Check the changelog for the latest updates.

Features

Deterministic Guid

The Guid5 class provides a method to generate deterministic GUIDs (UUID v5) based on the input arguments. This means that the same set of input arguments will always produce the same GUID, which can be useful for scenarios where consistent identifiers are needed across different systems or runs.

Example Usage

using Raccoon.Ninja.Tools.Uuid;
using System;

class Program
{
    static void Main()
    {
        // Example with one argument
        Guid guid1 = Guid5.NewGuid("example");
        Console.WriteLine(guid1);

        // Example with multiple arguments
        Guid guid2 = Guid5.NewGuid("example", 123, true);
        Console.WriteLine(guid2);

        // Example with different types of arguments
        Guid guid3 = Guid5.NewGuid("example", DateTime.Now);
        Console.WriteLine(guid3);
    }
}

Performance

Based on the benchmark results, generating a deterministic GUID using the Guid5 class is significantly slower than creating a regular GUID (UUID v4). Here are some key points:

  • Creating a regular GUID (RegularGuid) takes approximately 69.41 nanoseconds.
  • Generating a GUID with one argument (Guid5WithOneArg) takes approximately 558 nanoseconds, which is about 6 times slower than creating a regular GUID.
  • The performance decreases further as more arguments are added or when longer strings are used. For example, generating a GUID with four arguments including a string of 10,000 chars (Guid5WithFourArgsMixedIncLongString).

While the Guid5 class provides the benefit of deterministic GUIDs, it comes with a performance cost compared to generating regular GUIDs.

Keep in mind that we're talking about nanoseconds here, so the performance difference may not be significant, depending on your application, and you have the benefit of deterministic GUIDs.

You can execute the benchmarks yourself by running the Guid5Benchmarks class in the Raccoon.Ninja.Tools.Benchmark.Tests project.

BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4780/22H2/2022Update) Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores .NET SDK 8.0.401 [Host] : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2

Method Mean Error StdDev Median Ratio RatioSD Rank Gen0 Allocated Alloc Ratio
RegularGuid 88.35 ns 3.445 ns 10.16 ns 88.18 ns 1.01 0.17 1 - - NA
Guid5WithOneArg 558.56 ns 14.021 ns 41.34 ns 548.36 ns 6.41 0.88 2 0.0353 224 B NA
Guid5WithTwoArgsMixed 612.78 ns 15.601 ns 46.00 ns 625.46 ns 7.03 0.97 2 0.0477 304 B NA
Guid5WithTwoArgsOnlyStrings 598.57 ns 18.596 ns 54.83 ns 593.49 ns 6.87 1.01 2 0.0477 304 B NA
Guid5WithThreeArgsMixed 779.63 ns 19.215 ns 56.66 ns 756.28 ns 8.94 1.22 4 0.0629 400 B NA
Guid5WithThreeArgsOnlyStrings 665.40 ns 17.160 ns 50.60 ns 673.00 ns 7.63 1.06 3 0.0534 336 B NA
Guid5WithFourArgsMixedIncLongString 6,577.32 ns 198.268 ns 584.60 ns 6,485.69 ns 75.44 11.02 6 0.5417 3408 B NA
Guid5WithFourArgsOnlyStringsIncLongString 6,308.70 ns 167.784 ns 494.71 ns 6,168.23 ns 72.36 10.12 6 0.5341 3352 B NA
Guid5WithLongString 5,741.92 ns 168.282 ns 496.18 ns 5,824.81 ns 65.85 9.52 5 0.1907 1208 B NA

List Extensions

The ListExtensions class provides several useful extension methods for working with lists and other enumerable collections.

SafeAll<T>

Determines whether all elements of a sequence satisfy a condition safely.

Parameters:

  • source (IEnumerable<T>): An enumerable to test.
  • predicate (Func<T, bool>): A function to test each element for a condition.

Returns:

  • bool: True if every element of the source sequence passes the test in the specified predicate. If source is empty or null, returns false.

Example Usage:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
bool allEven = numbers.SafeAll(n => n % 2 == 0); // Output: False

var emptyList = new List<int>();
bool allEvenEmpty = emptyList.SafeAll(n => n % 2 == 0); // Output: False

List<int> nullList = null;
bool allEvenNull = nullList.SafeAll(n => n % 2 == 0); // Output: False

var numbers2 = new List<int> { 2, 4, 6, 8 };
bool allEven2 = numbers2.SafeAll(n => n % 2 == 0); // Output: True

ForEachWithIndex<T>

Returns an iterable list containing every item and its index.

Parameters:

  • source (IEnumerable<T>): The target enumerable collection.

Returns:

  • IEnumerable<(int index, T item)>: An enumerable containing tuples with the index and item.

Example Usage:

var list = new List<string> { "a", "b", "c" };
foreach (var (index, item) in list.ForEachWithIndex())
{
    Console.WriteLine($"Index: {index}, Item: {item}");
}

ContainsCaseInsensitive

Checks if the source contains the specified string, ignoring case.

Parameters:

  • source (IEnumerable<string>): The source of strings to search.
  • containsText (string): The string to search for.
  • nullValuesAreErrors (bool): If true, null values in the source will be treated as errors and will not match the search string. If false, null values in the source will be ignored.

Returns:

  • bool: True if the source contains the specified string (case-insensitive); otherwise, false.

Example Usage:

var list = new List<string> { "Hello", "world" };
bool containsHello = list.ContainsCaseInsensitive("hello");
Console.WriteLine(containsHello); // Output: True

Replace<T>

Replaces the first occurrence of an object in the source.

Parameters:

  • source (IList<T>): The source list.
  • oldObj (T): The old object to be replaced.
  • newObj (T): The new object that will replace the old one.

Returns:

  • bool: True if the object is replaced; false if the object is not found in the source.

Example Usage:

var list = new List<int> { 1, 2, 3 };
bool replaced = list.Replace(2, 4);
Console.WriteLine(replaced); // Output: True
Console.WriteLine(string.Join(", ", list)); // Output: 1, 4, 3

HasElements<T>

Determines whether the specified collection has any elements.

Parameters:

  • list (ICollection<T>): The collection to check for elements.

Returns:

  • bool: True if the collection is not null and contains one or more elements; otherwise, false.

Example Usage:

var numbers = new List<int> { 1, 2, 3 };
bool hasElements = numbers.HasElements(); // Output: True

var emptyList = new List<int>();
bool hasElements = emptyList.HasElements(); // Output: False

List<string> nullList = null;
bool hasElements = nullList.HasElements(); // Output: False

Shuffle<T>

Shuffles the list in place using the Fisher-Yates algorithm.

Parameters:

  • list (IList<T>): The list to shuffle.

Example Usage:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
numbers.Shuffle();
// numbers is now shuffled, e.g., { 3, 1, 5, 2, 4 }

Random<T>

Gets a random item from the list.

Parameters:

  • list (IList<T>): The list to get a random item from.

Returns:

  • T: A random item from the list.

Example Usage:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
int randomItem = numbers.Random();
// randomItem is now one of the elements in the list, e.g., 3

PopLast<T>

Removes and returns the last item from the list.

Parameters:

  • list (IList<T>): The list to remove the last item from.

Returns:

  • T: The last item from the list.

Example Usage:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
int lastItem = numbers.PopLast();
// lastItem is now 5, and numbers is now { 1, 2, 3, 4 }

PopFirst<T>

Removes and returns the first item from the list.

Parameters:

  • list (IList<T>): The list to remove the first item from.

Returns:

  • T: The first item from the list.

Example Usage:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
int firstItem = numbers.PopFirst();
// firstItem is now 1, and numbers is now { 2, 3, 4, 5 }

IndexOfMax<T>

Gets the index of the maximum element in the list.

Parameters:

  • list (IList<T>): The list to find the maximum element in.

Returns:

  • int: The index of the maximum element in the list.

Example Usage:

var numbers = new List<int> { 1, 3, 2, 5, 4 };
int maxIndex = numbers.IndexOfMax();
// maxIndex is now 3, as the maximum element is 5 at index 3

IndexOfMin<T>

Gets the index of the minimum element in the list.

Parameters:

  • list (IList<T>): The list to find the minimum element in.

Returns:

  • int: The index of the minimum element in the list.

Example Usage:

var numbers = new List<int> { 1, 3, 2, 5, 4 };
int minIndex = numbers.IndexOfMin();
// minIndex is now 0, as the minimum element is 1 at index 0

RemoveDuplicates<T>

Removes duplicates from the list while preserving order.

Parameters:

  • list (IList<T>): The list to remove duplicates from.

Example Usage:

var numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
numbers.RemoveDuplicates();
// numbers is now { 1, 2, 3, 4, 5 }

String Extensions

The StringExtensions class provides several useful extension methods for working with strings.

Minify

Minifies a text by replacing spaces, tabs, and line breaks with a single space.

Parameters:

  • bigText (string): The text to be minified.

Returns:

  • string: The minified text.

Example Usage:

string text = @"This is a   test.

                New line.";
string minifiedText = text.Minify();
Console.WriteLine(minifiedText); // Output: "This is a test. New line."

StripAccents

Removes all diacritics (accents) from a string.

Parameters:

  • text (string): The text from which to remove diacritics.

Returns:

  • string: The text without diacritics.

Example Usage:

string text = "Café";
string strippedText = text.StripAccents();
Console.WriteLine(strippedText); // Output: "Cafe"

OnlyDigits

Removes everything that is not a digit from a string.

Parameters:

  • text (string): The target string.

Returns:

  • string: A string containing only digits.

Example Usage:

string text = "Phone: 123-456-7890";
string digits = text.OnlyDigits();
Console.WriteLine(digits); // Output: "1234567890"

DateTime Extensions

The DateTimeExtensions class provides several useful extension methods for working with DateTime objects.

DaysSince

Calculates the number of days since the specified date.

Parameters:

  • date (DateTime): The date to calculate the days since.
  • currentDate (DateTime?): The current date to compare against. If null (not provided), the current date and time will be used. (Default: current datetime)
  • allowMixedDateTimeKind (bool): Indicates whether to allow mixed DateTimeKind values between the date and currentDate. (Default: false)

Returns:

  • int: The number of days between the specified date and the current date.

Example Usage:

var pastDate = new DateTime(2020, 1, 1);
var daysSince = pastDate.DaysSince(); // Calculates days since January 1, 2020 to today

Operation Result

The Result<TPayload> class represents the result of an operation, which can either be a success with a payload or a failure with an error. This class provides methods to map and process the result based on its success or failure state.

It's a somewhat functional approach to handling operation results, not fully adhering to the functional programming paradigm but providing some of its benefits.

Example Usage

using Raccoon.Ninja.Tools.OperationResult;
using Raccoon.Ninja.Tools.OperationResult.ResultError;

class Program
{
    static void Main()
    {
        // Example mapping result.
        var result = DoSomething();
        result.Map(
            success => Console.WriteLine($"Success! Here's the result: {success}"), // Only executed if successful
            error => Console.WriteLine($"Oh no! It failed! Error: {error.ErrorMessage}") // Only executed if failed
        );

        // Example processing result and getting an object back.
        var result2 = DoSomethingElseAndReturnPayload();
        var processedPayload = failureResult.Process(
            success => $"Processed: {success}", // Only executed if successful
            failure => $"Failed: {failure.ErrorMessage}" // Only executed if failed
        );
        Console.WriteLine(processedPayload);
    }
}

Performance

Instantiation

Quick update here. I caved in and changed from class to readonly struct. I think the performance benefits, thread safety, and immutability are worth it. Thankfully, this won't affect anyone, because the contracts are the same.

Below are the results of the benchmark tests for instantiating Classes, Struct, Readonly Struct, and Records. I'm aware that they are not the best, but it's good enough for a brief comparison.

Method Mean Error StdDev Median Rank Gen0 Allocated
NewClass 5.7458 ns 0.2447 ns 0.7216 ns 5.6876 ns 2 0.0051 32 B
NewStruct 0.0688 ns 0.0320 ns 0.0938 ns 0.0008 ns 1 - -
NewReadonlyStruct 0.0795 ns 0.0341 ns 0.0990 ns 0.0370 ns 1 - -
NewRecord 5.6249 ns 0.2198 ns 0.6482 ns 5.5565 ns 2 0.0051 32 B

To no one's surprise, instantiating a struct or readonly struct is by far the fastest option and won't allocate any memory.

Usage

For the usage benchmark test, I create the following scenarios:

  1. ThrowExceptionOnError: When an error occurs, an exception is thrown and captured by the caller;
  2. ReturnResultOnError: When an error occurs, the result is returned to the caller (instead of throwing an exception);
  3. ReturnNullOnExceptionCaught: When an error occurs, the result is returned as null to the caller;
  4. ReturnImplicitResultOnExceptionCaught: When an error occurs, the exception is implicitly converted to the Result type and returned to the caller;
  5. ReturnExplicitResultOnExceptionCaught: When an error occurs, the exception is explicitly converted to the Result type and returned to the caller;
  6. ReturnSuccessList: When the operation is successful, a list of string is returned to the caller;
  7. ReturnSuccessResultList: When the operation is successful, a list of string is returned as a Result type to the caller. The converstion is done implicitly.
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4780/22H2/2022Update)
Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK 8.0.401
[Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
Method Mean Error StdDev Median Ratio RatioSD Rank Gen0 Allocated Alloc Ratio
ThrowExceptionOnError 8,339.272 ns 211.8029 ns 614.4788 ns 8,358.530 ns 1.005 0.11 4 0.0458 344 B 1.00
ReturnResultOnError 8.545 ns 0.3371 ns 0.9834 ns 8.411 ns 0.001 0.00 1 0.0051 32 B 0.09
ReturnNullOnExceptionCaught 6,147.746 ns 121.6040 ns 318.2166 ns 6,139.373 ns 0.741 0.07 3 0.0305 224 B 0.65
ReturnImplicitResultOnExceptionCaught 6,095.540 ns 159.0650 ns 461.4763 ns 5,964.967 ns 0.735 0.08 3 0.0381 256 B 0.74
ReturnExplicitResultOnExceptionCaught 6,280.372 ns 124.5711 ns 332.5055 ns 6,357.890 ns 0.757 0.07 3 0.0381 256 B 0.74
ReturnSuccessList 22.468 ns 0.5101 ns 0.6451 ns 22.420 ns 0.003 0.00 2 0.0140 88 B 0.26
ReturnSuccessResultList 23.845 ns 0.5110 ns 1.4074 ns 23.536 ns 0.003 0.00 2 0.0140 88 B 0.26

In general terms, using the Operation Result to handle the results, in case of errors, is way faster than throwing an exception and uses less memory (because of the overhead involved in the exception handling). So that's a great idea when you don't need details from an exception and just want to communicate an error.

When capturing an unexpected exception, and returning a result (instead of null, empty list or something like that), from the benchmark, it seems like there's no significant difference between the approaches.

For cases of success, the overhead is minimal (about 1.3ns), which isn't really significant, and with no extra memory allocation.

From this, seems like the Operation Result is a good choice for handling errors and returning results in a standardized way.

Other

Shamelessly plugging the link to my site: https://raccoon.ninja

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.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.

Version Downloads Last updated
1.8.0 96 9/8/2024
1.7.0 103 9/7/2024
1.6.0 96 9/7/2024
1.5.0 98 9/7/2024
1.4.0 94 9/2/2024
1.3.0 96 9/1/2024
1.2.0 97 9/1/2024
1.1.0 100 8/31/2024

## [1.4.0] - Sep, 2024
- Added DateTime extension methods:
 - `DaysSince`: Returns the number of days since a given date (default: current date time);