C# LINQ and Pattern Matching challenge - c#

I need a solution that checks whether the content of a string of fixed lenght adheres to a set of rules. If not, I need to retrieve a list of the Rules that failed, the Expected value for each rule, and the Actually value contained within the string.
This is my current solution:
string actual = "628IDENTREGISTER153004085616P30062010EAPFEMPA013.1";
// Dictionary<Tuple<rule, expected>, startingPostion>
var expected = new Dictionary<Tuple<string, string>, int>
{
{new Tuple<string, string>("900052", "628"), 0},
{new Tuple<string, string>("9000250", "IDENTREGISTER1"), 3},
{new Tuple<string, string>("900092", "53004085616"), 17},
{new Tuple<string, string>("900004", "P"), 28},
{new Tuple<string, string>("900089", "30062010"), 29},
{new Tuple<string, string>("900028", "E"), 37},
{new Tuple<string, string>("900029", "A"), 38},
{new Tuple<string, string>("900002", "P"), 39},
{new Tuple<string, string>("900030", "FEMPA013.0"), 40}
};
// Create an IEnumerable of all broken rules
var result = expected.Where(field =>
!field.Key.Item2.Equals(
actual.Substring(field.Value, field.Key.Item2.Length)))
// Prints:
// [(900030, FEMPA013.0), 40]
foreach (var res in result)
Console.WriteLine(res);
I'm sure there’s a better way of solving this problem. Also, as it stands, I’m not entirely satisfied with this solution as it does not give me the actual field.
Thanks.

Any reason you couldn't just wrap the rule along with the inspected portion in a Tuple?
If not, I would do something like this:
var result = from field in expected
let inspected = actual.Substring(field.Value, field.Key.Item2.Length)
where !field.Key.Item2.Equals(inspected)
select (field, inspected);
Which would then, given your example above, output:
([(900030, FEMPA013.0), 40], FEMPA013.1)
You could unpack the rule entry a little further in the select as well, something along the lines of select (field.Key.Item1, field.Key.Item2, inspected);, and you'll end up with a tuple of (RuleId, expected, actual)

You should create a class to represent a rule, and have some helper methods in the class:
public class Rule {
public string RuleName;
public string Expected;
public int StartPos;
public bool IsMatch(string actual) => Field(actual) == Expected;
public string Field(string actual) => actual.Substring(StartPos, Math.Min(Expected.Length, actual.Length-StartPos));
public override string ToString() => $"{{ {RuleName}: #{StartPos}=\"{Expected}\" }}";
}
Now you can just need a List<Rule> to hold the rules:
var expected = new List<Rule> {
new Rule { RuleName = "900052", Expected = "628", StartPos = 0 },
new Rule { RuleName = "9000250", Expected = "IDENTREGISTER1", StartPos = 3 },
new Rule { RuleName = "900092", Expected = "53004085616", StartPos = 17 },
new Rule { RuleName = "900004", Expected = "P", StartPos = 28 },
new Rule { RuleName = "900089", Expected = "30062010", StartPos = 29 },
new Rule { RuleName = "900028", Expected = "E", StartPos = 37 },
new Rule { RuleName = "900029", Expected = "A", StartPos = 38 },
new Rule { RuleName = "900002", Expected = "P", StartPos = 39 },
new Rule { RuleName = "900030", Expected = "FEMPA013.0", StartPos = 40 }
};
And you can find the bad rules and extract the bad fields:
string actual = "628IDENTREGISTER153004085616P30062010EAPFEMPA013.1";
var result = expected.Where(rule => !rule.IsMatch(actual)).Select(rule => new { rule, Actual = rule.Field(actual) });
foreach (var res in result)
Console.WriteLine(res);
// Output is
// { rule = { 900030: #40="FEMPA013.0" }, Actual = FEMPA013.1 }

Related

c# how to create an array of an anonymous type? [duplicate]

In C# 3.0 you can create anonymous class with the following syntax
var o = new { Id = 1, Name = "Foo" };
Is there a way to add these anonymous class to a generic list?
Example:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List<var> list = new List<var>();
list.Add(o);
list.Add(o1);
Another Example:
List<var> list = new List<var>();
while (....)
{
....
list.Add(new {Id = x, Name = y});
....
}
You could do:
var list = new[] { o, o1 }.ToList();
There are lots of ways of skinning this cat, but basically they'll all use type inference somewhere - which means you've got to be calling a generic method (possibly as an extension method). Another example might be:
public static List<T> CreateList<T>(params T[] elements)
{
return new List<T>(elements);
}
var list = CreateList(o, o1);
You get the idea :)
Here is the answer.
string result = String.Empty;
var list = new[]
{
new { Number = 10, Name = "Smith" },
new { Number = 10, Name = "John" }
}.ToList();
foreach (var item in list)
{
result += String.Format("Name={0}, Number={1}\n", item.Name, item.Number);
}
MessageBox.Show(result);
There are many ways to do this, but some of the responses here are creating a list that contains garbage elements, which requires you to clear the list.
If you are looking for an empty list of the generic type, use a Select against a List of Tuples to make the empty list. No elements will be instantiated.
Here's the one-liner to create an empty list:
var emptyList = new List<Tuple<int, string>>()
.Select(t => new { Id = t.Item1, Name = t.Item2 }).ToList();
Then you can add to it using your generic type:
emptyList.Add(new { Id = 1, Name = "foo" });
emptyList.Add(new { Id = 2, Name = "bar" });
As an alternative, you can do something like below to create the empty list (But, I prefer the first example because you can use it for a populated collection of Tuples as well) :
var emptyList = new List<object>()
.Select(t => new { Id = default(int), Name = default(string) }).ToList();
Not exactly, but you can say List<object> and things will work. However, list[0].Id won't work.
This will work at runtime in C# 4.0 by having a List<dynamic>, that is you won't get IntelliSense.
If you are using C# 7 or above, you can use tuple types instead of anonymous types.
var myList = new List<(int IntProp, string StrProp)>();
myList.Add((IntProp: 123, StrProp: "XYZ"));
I guess
List<T> CreateEmptyGenericList<T>(T example) {
return new List<T>();
}
void something() {
var o = new { Id = 1, Name = "foo" };
var emptyListOfAnonymousType = CreateEmptyGenericList(o);
}
will work.
You might also consider writing it like this:
void something() {
var String = string.Emtpy;
var Integer = int.MinValue;
var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}
I usually use the following; mainly because you then "start" with a list that's empty.
var list = Enumerable.Range(0, 0).Select(e => new { ID = 1, Name = ""}).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
//etc.
Lately, I've been writing it like this instead:
var list = Enumerable.Repeat(new { ID = 1, Name = "" }, 0).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
Using the repeat method would also allow you to do:
var myObj = new { ID = 1, Name = "John" };
var list = Enumerable.Repeat(myObj, 1).ToList();
list.Add(new { ID = 2, Name = "Liana" });
..which gives you the initial list with the first item already added.
You can do this in your code.
var list = new[] { new { Id = 1, Name = "Foo" } }.ToList();
list.Add(new { Id = 2, Name = "Bar" });
I checked the IL on several answers. This code efficiently provides an empty List:
using System.Linq;
…
var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
In latest version 4.0, can use dynamic like below
var list = new List<dynamic>();
list.Add(new {
Name = "Damith"
});
foreach(var item in list){
Console.WriteLine(item.Name);
}
}
You can create a list of dynamic.
List<dynamic> anons=new List<dynamic>();
foreach (Model model in models)
{
var anon= new
{
Id = model.Id,
Name=model.Name
};
anons.Add(anon);
}
"dynamic" gets initialized by the first value added.
Here is a another method of creating a List of anonymous types that allows you to start with an empty list, but still have access to IntelliSense.
var items = "".Select( t => new {Id = 1, Name = "foo"} ).ToList();
If you wanted to keep the first item, just put one letter in the string.
var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
Here is my attempt.
List<object> list = new List<object> { new { Id = 10, Name = "Testing1" }, new {Id =2, Name ="Testing2" }};
I came up with this when I wrote something similar for making a Anonymous List for a custom type.
I'm very surprised nobody has suggested collection initializers. This way can only add objects when the list is created hence the name however it seems like the nicest way of doing it. No need to create an array then convert it to a list.
var list = new List<dynamic>()
{
new { Id = 1, Name = "Foo" },
new { Id = 2, Name = "Bar" }
};
You can always use object instead of dynamic but trying to keep it in a true generic way then dynamic makes more sense.
Instead of this:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List <var> list = new List<var>();
list.Add(o);
list.Add(o1);
You could do this:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List<object> list = new List<object>();
list.Add(o);
list.Add(o1);
However, you will get a compiletime error if you try to do something like this in another scope, although it works at runtime:
private List<object> GetList()
{
List<object> list = new List<object>();
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
list.Add(o);
list.Add(o1);
return list;
}
private void WriteList()
{
foreach (var item in GetList())
{
Console.WriteLine("Name={0}{1}", item.Name, Environment.NewLine);
}
}
The problem is that only the members of Object are available at runtime, although intellisense will show the properties id and name.
In .net 4.0 a solution is to use the keyword dynamic istead of object in the code above.
Another solution is to use reflection to get the properties
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var anonymous = p.GetList(new[]{
new { Id = 1, Name = "Foo" },
new { Id = 2, Name = "Bar" }
});
p.WriteList(anonymous);
}
private List<T> GetList<T>(params T[] elements)
{
var a = TypeGenerator(elements);
return a;
}
public static List<T> TypeGenerator<T>(T[] at)
{
return new List<T>(at);
}
private void WriteList<T>(List<T> elements)
{
PropertyInfo[] pi = typeof(T).GetProperties();
foreach (var el in elements)
{
foreach (var p in pi)
{
Console.WriteLine("{0}", p.GetValue(el, null));
}
}
Console.ReadLine();
}
}
}
You can do it this way:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
var array = new[] { o, o1 };
var list = array.ToList();
list.Add(new { Id = 3, Name = "Yeah" });
It seems a little "hacky" to me, but it works - if you really need to have a list and can't just use the anonymous array.
This is an old question, but I thought I'd put in my C# 6 answer. I often have to set up test data that is easily entered in-code as a list of tuples. With a couple of extension functions, it is possible to have this nice, compact format, without repeating the names on each entry.
var people= new List<Tuple<int, int, string>>() {
{1, 11, "Adam"},
{2, 22, "Bill"},
{3, 33, "Carol"}
}.Select(t => new { Id = t.Item1, Age = t.Item2, Name = t.Item3 });
This gives an IEnumerable - if you want a list that you can add to then just add ToList().
The magic comes from custom extension Add methods for tuples, as described at https://stackoverflow.com/a/27455822/4536527.
public static class TupleListExtensions {
public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
T1 item1, T2 item2) {
list.Add(Tuple.Create(item1, item2));
}
public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
T1 item1, T2 item2, T3 item3) {
list.Add(Tuple.Create(item1, item2, item3));
}
// and so on...
}
The only thing I don't like is that the types are separated from the names, but if you really don't want to make a new class then this approach will still let you have readable data.
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);
For your second example, where you have to initialize a new List<T>, one idea is to create an anonymous list, and then clear it.
var list = new[] { o, o1 }.ToList();
list.Clear();
//and you can keep adding.
while (....)
{
....
list.Add(new { Id = x, Name = y });
....
}
Or as an extension method, should be easier:
public static List<T> GetEmptyListOfThisType<T>(this T item)
{
return new List<T>();
}
//so you can call:
var list = new { Id = 0, Name = "" }.GetEmptyListOfThisType();
Or probably even shorter,
var list = new int[0].Select(x => new { Id = 0, Name = "" }).Tolist();
Deriving from this answer, I came up with two methods that could do the task:
/// <summary>
/// Create a list of the given anonymous class. <paramref name="definition"/> isn't called, it is only used
/// for the needed type inference. This overload is for when you don't have an instance of the anon class
/// and don't want to make one to make the list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="definition"></param>
/// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
public static List<T> CreateListOfAnonType<T>(Func<T> definition)
#pragma warning restore RECS0154 // Parameter is never used
{
return new List<T>();
}
/// <summary>
/// Create a list of the given anonymous class. <paramref name="definition"/> isn't added to the list, it is
/// only used for the needed type inference. This overload is for when you do have an instance of the anon
/// class and don't want the compiler to waste time making a temp class to define the type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="definition"></param>
/// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
public static List<T> CreateListOfAnonType<T>(T definition)
#pragma warning restore RECS0154 // Parameter is never used
{
return new List<T>();
}
You can use the methods like
var emptyList = CreateListOfAnonType(()=>new { Id = default(int), Name = default(string) });
//or
var existingAnonInstance = new { Id = 59, Name = "Joe" };
var otherEmptyList = CreateListOfAnonType(existingAnonInstance);
This answer has a similar idea, but I didn't see it until after I made those methods.
Using Reflection
Microsoft documentation about this topic.
using System;
using System.Collections;
using System.Collections.Generic;
var anonObj = new { Id = 1, Name = "Foo" };
var anonType = anonObj.GetType();
var listType = typeof(List<>);
// We know that List<> have only one generic argument, so we do this:
var contructed = listType.MakeGenericType(anonType);
// Create instance
var instance = Activator.CreateInstance(contructed);
// Using it
var list = (IList)instance;
list.Add(anonObj);
For Dictionary<,> you need to pass 2 arguments
Ex.: dicType.MakeGenericType( type1, type2 )
And for generic types with constraints (where T : struct),
we need to do more verifications. Check microsoft docs to learn how.
Try with this:
var result = new List<object>();
foreach (var test in model.ToList()) {
result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}

how to set Number from 0 to increment 1,2,,... etc for combine dictionary

I have 2 dictionaries where Number = 0 for all items,
var dict1 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key2", new Test { Number = 0, Name = "Name2" } },
{ "Key3", new Test { Number = 0, Name = "Name3" } }
};
var dict2 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key4", new Test { Number = 0, Name = "Name4" } }
};
Now after eliminating duplicate key/value pairs, in combined dictionary result I want to set Number = 1, 2, 3,... how to do this?
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
You can do
var n = 0;
and then do it functionally but not very efficiently in your case. The function will select all items from your dictionary and create a new collection with updated values, which is then converted to a dictionary.
var newDict = dict2.Select(d => new Test { Number = ++n, Name = d.Value[1].Name }).ToDictionary();
Or with a good old loop:
foreach(var d in dict2)
{
d.Value[0].Number = n++;
}
As suggested by the comment. If you want to start off with 0, use
n++;
if with 1, use
++n;
Try this:
int i = 0;
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.OrderBy(kvp => kvp.Key)
.ToDictionary(kvp => kvp.Key, kvp => new Test() { Number = ++i, Name = kvp.First().Value.Name });
It should give you this:
{ "Key1", new Test { Number = 1, Name = "Name1" } },
{ "Key2", new Test { Number = 2, Name = "Name2" } },
{ "Key3", new Test { Number = 3, Name = "Name3" } }
{ "Key4", new Test { Number = 4, Name = "Name4" } }
I think you're trying to combine dictionaries, and then assign to Number the count of duplicate items.
You might consider putting all the dictionaries into a list and iterating over each item and putting it in a combined result dictionary. If the item already exists in the result then increment the Number property.
Initial setup:
public class Test
{
public int Number { get; set; }
public string Name { get; set; }
}
var dict1 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key2", new Test { Number = 0, Name = "Name2" } },
{ "Key3", new Test { Number = 0, Name = "Name3" } }
};
var dict2 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key4", new Test { Number = 0, Name = "Name4" } }
};
// Put the dictionaries you want to combine into one list:
var all = new List<Dictionary<string, Test>>();
all.Add(dict1);
all.Add(dict2);
// Declare result dictionary
var combine = new Dictionary<string, Test>();
Set up is done, this is the main loop you want:
foreach (var dict in all)
{
foreach (var kvp in dict)
{
if (combine.ContainsKey(kvp.Key))
{
combine[kvp.Key].Number++;
}
else
{
combine.Add(kvp.Key, kvp.Value);
}
}
}
Interactive shell output:
Dictionary<string, Submission#0.Test>(4) {
{ "Key1", Submission#0.Test { Name="Name1", Number=1 } },
{ "Key2", Submission#0.Test { Name="Name2", Number=0 } },
{ "Key3", Submission#0.Test { Name="Name3", Number=0 } },
{ "Key4", Submission#0.Test { Name="Name4", Number=0 } }
}
There is an overload of Select extension which provides index as well (MSDN)
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select((grp,index) => new { Key = grp.Key, Value = new Test { Number = index+1, Name = grp.First().Name}})
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
You can loop through the keys
int n = 1;
foreach (string key in combine.Keys) {
combine[key].Number = n++;
}
The keys are not returned in order. If you want to number them in order:
int n = 1;
var orderedKeyValuePairs = combine
.OrderBy(kvp => kvp.Key, StringComparer.CurrentCultureIgnoreCase);
foreach (var kvp in orderedKeyValuePairs) {
kvp.Value.Number = n++;
}
Note that you can only access the Number like this if Test is a reference type (class). If Test was a struct, you would have to re-assign the whole struct because the dictionary would return a copy of the value.
The optional StringComparer argument allows you to specify different string comparison modes:
Ordinal, OrdinalIgnoreCase, CurrentCulture, CurrentCultureIgnoreCase
If you want to sort by name:
int n = 1;
var orderedValues = combine.Values
.OrderBy(v => v.Name, StringComparer.CurrentCultureIgnoreCase);
foreach (var v in orderedValues) {
v.Number = n++;
}
Looping over the key-value-pairs or values also has the advantage that you can change the value directly, whereas when looping through the keys (as in my first code snippet), you must look up the dictionary, which is less performing.

Using LINQ get all possible pair of connections from two lists

Given lists Input = {A, B} and Output = {1, 2, 3, 4 }, I want to get a new list that contains all possible pair of connections {connection1,connection2} :
connections = {A1, B2},
{A1, B3},
{A1, B4},
{A2, B1},
{A2, B3},
{A2, B4},
{A3, B1},
{A3, B2},
{A3, B4},
{A4, B1},
{A4, B2},
{A4, B3}
Rules:
Each combination represents a couple of 2 connections.
Each connection is represented by 1 input and 1 output. (for example, AB is not possible)
If an input or output element is already used from the previous connection then it can't be used in the next one. (for example, {A1,B1} is not possible)
Input can contain n elements (n>=2) and Output can contain m elements (m>=2) but the combination is always represented by only 2 connections.
Illustration of connection {A1, B3}
Suggestions?
Updated Answer
This should do it:
using System.Linq;
using static System.Console;
class Program {
static void Main(string[] args) {
var inputs = new[] { "A", "B", "C" };
var outputs = new[] { "1", "2", "3", "4" };
var res = from i1 in inputs
from i2 in inputs
where i1 != i2
from o1 in outputs
from o2 in outputs
where o1 != o2
let c1 = i1 + o1
let c2 = i2 + o2
// Avoid {x,y} and {y,x} in result.
where c1.CompareTo(c2) < 0
select (first: c1, second: c2);
foreach (var r in res) {
WriteLine($"{{{r.first}, {r.second}}}");
}
}
}
Original Answer
You need the LINQ to Objects equivalent of a cross join, which is just looping over the contents of both lists without any conditions to limit the set of results.
var allPairs = (from a in ListA
from b in ListB
select (a, b)
).ToList();
Will give you a list of all pairs as tuples.
In your case you seem to want all pairs of pairs: given all combinations of input and output then get all pairs of combinations on input and output.
Which is just a case of expanding the above with a second combination of the list of all input-output combinations.
// Assume `Input` and `Output` and enumerables of string
var inputOutputPairs = (from ip in Input
from op in Output
select ip + op
).ToList();
var result = (from left in inputOutputPairs
from right in inputOutputPairs
select (left, right)
// To avoid duplicates like ("A4","A4") include this:
// where left != right
).ToList();
And the result will be a list of ValueTuple<string, string>.
Richard's updated answer is elegant and probably the best fit for your needs, but I suggest an alternative idea using combinatorics. (and also using function-style linq which is imho a lot easier to debug and maintain).
The idea is:
get all valid input combinations (of length 2)
get all valid output variations (of length 2)
combine all valid inputs with all output variations.
Example implementation using a pre-baked combinatorics package from NuGet:
var Input = new[] { "A", "B"};
var Output = new[] { "1", "2", "3", "4" };
int maxConnections = 2;
var validInputs = new Combinations<String>(Input, maxConnections);
var validOutputs = new Variations<String>(Output, maxConnections);
var connectionsets = validInputs
.SelectMany(ins => validOutputs
.Select(outs => new { Ins = ins, Outs = outs })
);
To get the connection from the format of ins/outs to single string, you could use something along the lines of :
String.Join(",", set.Ins.Select((input, i) => input + set.Outs.Skip(i).First()));
NB! Also note that this approach enables you to solve a bit wider question of finding N connections instead of just 2.
I've written an unit test with the example you provide and a working implementation:
public static class PairsOfConnections
{
public static IEnumerable<Tuple<string, string>> GetAllPairsOfConnections(string[] input, string[] output)
{
var connectionsFromFirstInput = output.Select(o => new { Input = input[0], Output = o });
var connectionsFromSecondInput = output.Select(o => new { Input = input[1], Output = o }).ToList();
return from a in connectionsFromFirstInput
from b in connectionsFromSecondInput
where a.Output != b.Output
select new Tuple<string, string>(a.Input + a.Output, b.Input + b.Output);
}
}
public class PairsOfConnectionsTests
{
[Test]
public void TestGetAllPairsOfConnections()
{
string[] input = { "A", "B" };
string[] output = { "1", "2", "3", "4" };
IEnumerable<Tuple<string, string>> result = PairsOfConnections.GetAllPairsOfConnections(input, output);
var expected = new List<Tuple<string, string>>
{
new Tuple<string, string>("A1","B2"),
new Tuple<string, string>("A1","B3"),
new Tuple<string, string>("A1","B4"),
new Tuple<string, string>("A2","B1"),
new Tuple<string, string>("A2","B3"),
new Tuple<string, string>("A2","B4"),
new Tuple<string, string>("A3","B1"),
new Tuple<string, string>("A3","B2"),
new Tuple<string, string>("A3","B4"),
new Tuple<string, string>("A4","B1"),
new Tuple<string, string>("A4","B2"),
new Tuple<string, string>("A4","B3")
};
CollectionAssert.AreEquivalent(expected, result);
}
}
Seeing that you have clarified that there can be more than two inputs, I've written a modified algorithm, with the same unit test as before, and a new one:
public static class PairsOfConnections
{
public static IEnumerable<Tuple<string, string>> GetAllPairsOfConnections(string[] inputs, string[] outputs)
{
var connectionsFromFirstInput = outputs.Select(o => new { Input = inputs[0], Output = o }).ToList();
var result = new List<Tuple<string, string>>();
foreach (string input in inputs.Skip(1))
{
var connectionsFromNextInput = outputs.Select(output => new { Input = input, Output = output }).ToList();
IEnumerable<Tuple<string, string>> pairs = from a in connectionsFromFirstInput
from b in connectionsFromNextInput
where a.Output != b.Output
select new Tuple<string, string>(a.Input + a.Output, b.Input + b.Output);
result.AddRange(pairs);
}
return result;
}
}
public class PairsOfConnectionsTests
{
[Test]
public void TestGetAllPairsOfConnections_WithTwoInputs()
{
string[] input = { "A", "B" };
string[] output = { "1", "2", "3", "4" };
IEnumerable<Tuple<string, string>> result = PairsOfConnections.GetAllPairsOfConnections(input, output);
var expected = new List<Tuple<string, string>>
{
new Tuple<string, string>("A1","B2"),
new Tuple<string, string>("A1","B3"),
new Tuple<string, string>("A1","B4"),
new Tuple<string, string>("A2","B1"),
new Tuple<string, string>("A2","B3"),
new Tuple<string, string>("A2","B4"),
new Tuple<string, string>("A3","B1"),
new Tuple<string, string>("A3","B2"),
new Tuple<string, string>("A3","B4"),
new Tuple<string, string>("A4","B1"),
new Tuple<string, string>("A4","B2"),
new Tuple<string, string>("A4","B3")
};
CollectionAssert.AreEquivalent(expected, result);
}
[Test]
public void TestGetAllPairsOfConnections_WithThreeInputs()
{
string[] input = { "A", "B", "C" };
string[] output = { "1", "2", "3" };
IEnumerable<Tuple<string, string>> result = PairsOfConnections.GetAllPairsOfConnections(input, output);
var expected = new List<Tuple<string, string>>
{
new Tuple<string, string>("A1","B2"),
new Tuple<string, string>("A1","B3"),
new Tuple<string, string>("A1","C2"),
new Tuple<string, string>("A1","C3"),
new Tuple<string, string>("A2","B1"),
new Tuple<string, string>("A2","B3"),
new Tuple<string, string>("A2","C1"),
new Tuple<string, string>("A2","C3"),
new Tuple<string, string>("A3","B1"),
new Tuple<string, string>("A3","B2"),
new Tuple<string, string>("A3","C1"),
new Tuple<string, string>("A3","C2"),
};
CollectionAssert.AreEquivalent(expected, result);
}
}

how can I sort a custom list depending on both integer and character value

I have a list of custom object, one property in this list is a string which can contain integers or integer and then a character. Like this:
1, 1A, 2, 3, 4, 4B, 4A
How can I sort this list so it sort according to this..
1, 1A, 2, 3, 4, 4A, 4B
I have checked the following link... Sort array list by numbers then by letters but that only handle an array of string, mine is a list object with several properties..
This is my custom object list.
var myList = new List<Article>
{
new Article {Position = "1", Info = "test1"},
new Article {Position = "2", Info = "test2"},
new Article {Position = "3", Info = "test3"},
new Article {Position = "4", Info = "test4"},
new Article {Position = "4B", Info = "test5"},
new Article {Position = "4A", Info = "test6"}
};
I'm taking the custom IComparer<T> implemented by #MehrzadChehraz in this answer (if this helps, make sure to give him the credit), and adding the missing piece which is simply to use the overload of Enumerable.OrderBy which takes a custom IComparer<T>:
void Main()
{
var myList = new List<Article>
{
new Article {Position = "1", Info = "test1"},
new Article {Position = "2", Info = "test2"},
new Article {Position = "3", Info = "test3"},
new Article {Position = "4", Info = "test4"},
new Article {Position = "4B", Info = "test5"},
new Article {Position = "4A", Info = "test6"}
};
var sortedList = myList.OrderBy(x => x.Position, new CustomComparer()).ToList();
}
public class CustomComparer : IComparer<string>
{
Comparer _comparer = new Comparer(System.Globalization.CultureInfo.CurrentCulture);
public int Compare(string x, string y)
{
string numxs = string.Concat(x.TakeWhile(c => char.IsDigit(c)).ToArray());
string numys = string.Concat(y.TakeWhile(c => char.IsDigit(c)).ToArray());
int xnum;
int ynum;
if (!int.TryParse(numxs, out xnum) || !int.TryParse(numys, out ynum))
{
return _comparer.Compare(x, y);
}
int compareNums = xnum.CompareTo(ynum);
if (compareNums != 0)
{
return compareNums;
}
return _comparer.Compare(x, y);
}
}
Which yields:
First we can define a list extension for "natural sorting", using the Windows API method StrCmpLogicalW():
public static class ListExt
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string lhs, string rhs);
// Version for lists of any type.
public static void SortNatural<T>(this List<T> self, Func<T, string> stringSelector)
{
self.Sort((lhs, rhs) => StrCmpLogicalW(stringSelector(lhs), stringSelector(rhs)));
}
// Simpler version for List<string>
public static void SortNatural(this List<string> self)
{
self.Sort(StrCmpLogicalW);
}
}
You can then in-place sort your list like so:
myList.SortNatural(x => x.Position);
Note: You don't need the public static void SortNatural(this List<string> self) method for this example, but I included it for completeness.
For Simplicity, you can use Regex in combination with Linq:
Regex rgxNumber = new Regex(#"\d+");
Regex rgxCharacters = new Regex("[a-zA-Z]+");
Func<string, int> getNumberPart = str => int.Parse(rgxNumber.Match(str).ToString());
Func<string, string> getCharactersPart = str => rgxCharacters.Match(str).ToString();
var orderd = myList.OrderBy(x => getNumberPart(x.Position))
.ThenBy(x => getCharactersPart(x.Position)).ToList();
This assumes no data in the form 1B2 is present.

C# Parse anonymous type to List of string values

I have an array of such object:
object[] internationalRates = new object[]
{
new { name = "London", value = 1 },
new { name = "New York", value = 2 } ,
etc...
};
I need to get List<string> of countries (or Dictionary<int, string> of pairs). How can I cast it?
You can use the dynamic keyword in this case:
var result = internationalRates.ToDictionary(
x => ((dynamic)x).value,
x => ((dynamic)x).name);
This produces a Dictionary with key/value pairs.
Warning:
The key and value are both of type dynamic, which I sorta don't like. It could potentially lead to run-time exceptions if you don't pay attention to the original types.
For example, this works fine:
string name = result[2]; // name == "New York"
This will also compile just fine, but will throw a run-time exception:
int name = result[2]; // tries to convert string to int, doesn't work
If you don't want to use dynamic you can write your own implementation of ToDictionary method with use of Reflection. Something like this :
public static class Helper
{
public static Dictionary<T1, T2> ToDictionary<T1, T2>(this IEnumerable<object> dict, string key, string value)
{
Dictionary<T1, T2> meowDict = new Dictionary<T1, T2>();
foreach (object item in dict)
meowDict.Add((T1)item.GetType().GetProperty(key).GetValue(item),
(T2)item.GetType().GetProperty(value).GetValue(item));
return meowDict;
}
}
Usage example :
object[] internationalRates = new object[]
{
new { name = "London", value = 1 },
new { name = "New York", value = 2 } ,
};
var dict = internationalRates.ToDictionary<int, string>("value", "name");
foreach (KeyValuePair<int, string> item in dict)
Console.WriteLine(item.Key + " " + item.Value);
Console.ReadLine();
Output :
1 London
2 New York
Change your definition to this:
var internationalRates = new []
{
new { name = "London", value = 1 },
new { name = "New York", value = 2 } ,
};
And then you can use this:
var res = internationalRates.ToDictionary(x => x.value, x => x.name);

Categories