Skip to content

jeromerg/NUtil

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Status: Build status NuGet

(Related project: NCase, NDocUtil)

NUtil

Various C# Utility classes initially developed for the NCase project.

Installation

In the Nuget Package Manager Console:

Install-Package NUtil

Content

Here is the list of utilities:

Pairwise Generator

Consider (S1, S2 ... Sn) n finite sets, the PairwiseGenerator generates an enumeration of tuples (s1, s2 ... sn) which ensures that any pair (si, sj) from any pair of set (Si, Sj) appears at least once.

var setCardinals = new int[] {3, 2, 2};

IEnumerable<int[]> tuples = new PairwiseGenerator().Generate(setCardinals);

foreach (int[] t in tuples)
    Console.WriteLine("Tuple #{0}:  {1}, {2}, {3}", i++, t[0], t[1], t[2]);

Outout:

Tuple #0:  0, 0, 0
Tuple #1:  0, 1, 1
Tuple #2:  1, 0, 0
Tuple #3:  1, 1, 1
Tuple #4:  2, 0, 0
Tuple #5:  2, 1, 1
Tuple #6:  0, 0, 1
Tuple #7:  0, 1, 0

This pairwise generator is used in NCase as an alternative to the default cartesian product, in order to reduce the amount of generated test cases. More about pairwise testing here and there.

About the algorithm

  • The algorithm tries to distribute the re-use of pairs among the whole set of pairs
  • The generation of tuple is lazy, resulting in low initial pre-processing time (only the available set of pairs is generated at startup time)
  • Further quantitative and qualitative analysis as well as comparison with existing algorithms would be required to precisely evaluate the properties of the algorithm

ForEach (Linq)

Every utility framework re-implements the following ForEach extension method:

var set = Enumerable.Range(0, 10);

set.ForEach(v => Console.Write(v));                

Output:

0123456789

The following overload enables placing an action between the processing of the items:

var set = Enumerable.Range(0, 10);

set.ForEach( v => Console.Write(v), 
            () => Console.Write(", "));                

Output:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

It is an alternative to the Linq Aggregate(...) function and the string.Join(...) static method, in order to perform aggregations. Following usage has no counterpart in the C# framework:

var set = Enumerable.Range(0, 10);

set.ForEach( v => SendToServer(v), 
            () => Thread.Sleep(10));                

Quadratic Processing (Linq)

CartesianProduct

var set1 = new [] {"a", "b"};
var set2 = new [] {0, 1};

var product = set1.CartesianProduct(set2, (s1, s2) => new { s1, s2 });

foreach (var pair in product)
    Console.WriteLine("({0}, {1})", pair.s1, pair.s2);

Output:

(a, 0)
(a, 1)
(b, 0)
(b, 1)

Remark: it is nothing else than a wrapper around:

return from in1 in first
       from in2 in second
       select selector(in1, in2);

But the wrapper enables to elegantly chain the cartesian product with other transformations.

TriangularProductWithoutDiagonal

var set1 = new [] {0, 1, 2};

var product = set1.TriangularProductWithoutDiagonal((s1, s2) => new { s1, s2 });

foreach (var pair in product)
    Console.WriteLine("({0}, {1})", pair.s1, pair.s2);

Output:

(0, 1)
(0, 2)
(1, 2)

Processing of Chained Dictionaries

Let's say, you need a model for the triplet (country, city, street). You can use the following model:

var stats = new Dictionary<string,               // Country
                    Dictionary<string,           // City
                            HashSet<string>>>(); // Street

The purpose of the CascadeExtensions class is to provide extension methods that reduces as much as possible the need of if-else statements to handle this kind of model composed of chained dictionaries.

CascadeAdd

model.CascadeAdd("FR").CascadeAdd("Paris").Add("Rue de la paix");
model.CascadeAdd("FR").CascadeAdd("Paris").Add("Rue de Paradis");
model.CascadeAdd("DE").CascadeAdd("Mainz").Add("Gutenbergplatz");

CascadeRemove

bool isRemoved1 = model
    .CascadeRemove("FR")
    .CascadeRemove("Paris")
    .CascadeRemove("Rue de la paix");

Console.WriteLine("isRemoved1= {0}", isRemoved1);

bool isRemoved2 = model
    .CascadeRemove("FR")
    .CascadeRemove("Paris")
    .CascadeRemove("Trafalgar Square");

Console.WriteLine("isRemoved2= {0}", isRemoved2);

Output:

isRemoved1= True
isRemoved2= False

Unsafe Indexer

You can get items from the model by using the indexer defined in the Dictionary class. But this indexer is unsafe: it throws an exception if the key does not exist.

var streets1 = model["FR"]["Paris"];

Console.WriteLine("Street in Paris: {0}", string.Join(", ", streets1));

try
{
    var streets2 = model["Switzerland"]["Lausanne"]; // UNREGISTERED COUNTRY!
}
catch (KeyNotFoundException e)
{
    Console.WriteLine("Streets in Lausanne: KeyNotFoundException has been thrown");
}

Output:

Street in Paris: Rue de la paix, Rue de Paradis
Streets in Lausanne: KeyNotFoundException has been thrown

CascadeGetOrDefault

If you need a safe get implementation, you can use the .CascadeGetOrDefault() extension method:

var safeStreets1  = model.CascadeGetOrDefault("FR")
                         .CascadeGetOrDefault("Paris");

Console.WriteLine("Streets in Paris : {0}", string.Join(", ", safeStreets1));

var safeStreets2 = model.CascadeGetOrDefault("Switzerland") // UNREGISTERED COUNTRY!
                        .CascadeGetOrDefault("Lausanne"); 

if(safeStreets2 == null)
    Console.WriteLine("No street found in Lausanne");

Output:

Streets in Paris : Rue de la paix, Rue de Paradis
No street found in Lausanne

CascadeTryFirst

string country,city, street;
bool ok = model.CascadeTryFirst(out country)
               .CascadeTryFirst(out city)
               .CascadeTryFirst(out street);

Console.WriteLine("CascadeTryFirst: ok={0}, country={1}, city={2}, street={3}", 
                  ok, country, city, street);
CascadeTryFirst: ok=True, country=FR, city=Paris, street=Rue de la paix

String Processing

Lines and JoinLines

The .Lines() extension method enables splitting a string into an enumeration of lines, whereas .JoinLines() enables to join a enumeration of lines into a single string:

string txt = "one line\nand a second line";
IEnumerable<string> lines = txt.Lines();
string rejoinedLines = lines.JoinLines();

Desindent

The .Desindent() allows to removed the indentation of a string:

string txt = "    I was originally indented!";

string desindentedTxt = txt.Desindent(tabIndentation:4);

Console.WriteLine("Before: {0}\nAfter :{1}", txt, desindentedTxt);

Output:

Before:     I was originally indented!
After :I was originally indented!

About

Various C# utility classes used in NCase project

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages