I'm new to C# and need some help with comparing collections. I have two List<string>
collections with their contents as below:
Collection Old:
{"AAA","BBB","CCC"}
Collection New:
{"BBB","CCC","DDD"}
I want to get a collection like below:
Collection Final:
{"AAA", "Remove"; "BBB", "Keep"; "CCC", "Keep"; "DDD", "Add"}
How can I do this?
old.Except(new) will give you those items to remove
new.Except(old) will give you items to add
old.Intersect(new) will give you items to keep
(This is assuming you don't mind using the System.Linq namespace)
Or if you prefer, you can consider each item individually and check the existence in each list
var oldList = new List<String>() {"AAA", "BBB", "CCC"};
var newList = new List<String>() {"BBB", "CCC", "DDD"};
var diffDictionary = new Dictionary<string, string>();
foreach (var oldEntry in oldList)
{
diffDictionary.Add(oldEntry, "Remove");
}
foreach (var newEntry in newList)
{
if (diffDictionary.ContainsKey(newEntry))
{
diffDictionary[newEntry] = "Keep";
}
else
{
diffDictionary.Add(newEntry, "Add");
}
}
foreach (var dDico in diffDictionary)
{
Console.WriteLine(string.Concat("Key: ", dDico.Key, " Value: ", dDico.Value));
}
You can use a dictionary to do this...
at the end, each element in the dictionary will tell you how many items of each kind were removed or added.
It will indicate this with a count, not a simple 3 state flag... that is because you may have added or removed repeated items... what if you insert 3 AAA's in the second collection.
string[] col1 = new string[] { "AAA", "BBB", "CCC" };
string[] col2 = new string[] { "BBB", "CCC", "DDD" };
Dictionary<string, int> colDic = new Dictionary<string, int>();
foreach (var item in col1)
{
int num;
if (colDic.TryGetValue(item, out num))
colDic[item] = num - 1;
else
colDic[item] = -1;
}
foreach (var item in col2)
{
int num;
if (colDic.TryGetValue(item, out num))
colDic[item] = num + 1;
else
colDic[item] = 1;
}
The end result will look like this:
AAA = -1
BBB = 0
CCC = 0
DDD = 1
In 1 line (sort of)!
string[] colOld = {"AAA","BBB","CCC"};
string[] colNew = {"BBB","CCC","DDD"};
dynamic colALL = (from o in colOld.Union(colNew)
select new {Value = o, Action =
colOld.Any(s => s == o) ?
colNew.Any(s => s == o) ? "Keep" : "Remove"
: "Add"
}).ToList();
Note: This is a developer fusion conversionof the below vb.net which does work - I've not had chance to test the c# version:
Dim colOld() As String = {"AAA", "BBB", "CCC"}
Dim colNew() As String = {"BBB", "CCC", "DDD"}
Dim colALL = (From o As String In colOld.Union(colNew) _
Select New With {.Value = o, .Action = _
If(colOld.Any(Function(s) s = o), _
If(colNew.Any(Function(s) s = o), "Keep", "Remove"), _
"Add")}).ToList
If you have this method
public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences)
{
return sequences.SelectMany(x => x);
}
you should be able to write:
static readonly string Remove = "Remove";
static readonly string Keep = "Keep";
static readonly string Add = "Add";
var result = Concat
(
old.Except(new).Select(x => new { x, Remove }),
old.Intersect(new).Select(x => new { x, Keep }),
new.Except(old).Select(x => new { x, Add })
);
Of course you can use the built-in Enumerable.Concat method but I find mine more elegant.
Related
Currently, I have implemented two lists with a double for loop to find matches between the two lists so I can join on them.
I have a list A which contains an ID and some other columns. I have a list B which contains an ID and some other columns. I have currently implemented a for loop within a for loop in order to make the comparisons for all the IDs so that I can find the ones that match and then return the joined results. I know want to understand how to implement a dictionary in this case as that will be more efficient to fix this problem.
public IEnumerable<Details> GetDetails(string ID)
{
// there are two lists defined up here
for (var item in listA)
{
for (var item2 in listB)
{
if (item.ID == item2.ID)
{
item.Name = item2.name;
}
}
}
return results;
}
Instead of having this double for loop, which is very inefficient. I want to learn how to implement a dictionary to fix this problem.
The dictionary would use the ids as keys (or indexes) so
Dictionary<string, object> myListA = new Dictionary<string, object>();
Dictionary<string, object> myListB = new Dictionary<string, object>();
public object GetDetails(string ID)
{
object a = myListA[ID];
object b = myListB[ID];
// combine them here how you want
// object c = a + b;
return c;
}
How about using linq to achieve your actual requirement? Something like:
public IEnumerable<A> GetDetails(int ID)
{
var listA = new List<A>
{
new A(){ ID = 1, Name = 2 },
new A(){ ID = 3, Name = 4 },
new A(){ ID = 5, Name = 6 },
};
var listB = new List<B>
{
new B(){ X = 1, name = 0 },
new B(){ X = 3, name = 1 }
};
return listA.Join(listB, k => k.ID, k => k.ID, (item, item2) =>
{
item.Name = item2.name;
return item;
}).Where(w => w.ID == ID);
}
If you just want the common IDs in the two lists, you can achieve that like this:
var commonIds = listA.Select(o => o.ID).Intersect(listB.Select(o => o.ID));
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);
}
}
I'd like to split my data to lists by an attribute's value and check all the combination options between the lists' items.
My problems are that I don't know how many list I'll get and if there is a better way to do that beside this way:
var a = Data.Descendants("value").Where(x => x.Attribute("v").Value == "1").ToList();
var b = Data.Descendants("value").Where(x => x.Attribute("v").Value == "2").ToList();
var c = Data.Descendants("value").Where(x => x.Attribute("v").Value == "3").ToList();
foreach (var tempA in a)
{
foreach (var tempB in b)
{
foreach (var tempC in c)
{
DO SOMETHING;
}
}
}
EDIT:
I'd like to check my items from one data source (var items = new List<string>{"1","1","2","3","2","1","3","3","2"})
Now I'd like to split this list to 3 lists (list a = "1","1","1" - list b = "2","2","2" - list c = "3","3","3")
In this step what I'm trying to do is to check all the combination from one item in one list to the other items in the other lists.
a[0] with b[0] c[0]
a[0] with b[0] c[1]
a[0] with b[0] c[2]
a[0] with b[1] c[0]
.
.
b[1] with a[2] c[2]
.
.
Thanks!
Could you try using the LINQ GroupBy method? Some examples are here:
LINQ GroupBy examples
You can use GroupBy to group your elements. Then you can create combinations using Linq.
var grouping = Data.Descendants("value")
.GroupBy(x => x.Attribute("v").Value);
var combinations grouping.SelectMany(x =>
grouping.Select(y =>
new { Group = x, Combination = y }));
foreach(var c in combinations)
{
//Do Something
}
e.g.
public class Pair
{
public string A { get; set; }
public string B { get; set; }
}
var pairs = new List<Pair>();
pairs.Add(new Pair { A = "1", B = "2" });
pairs.Add(new Pair { A = "1", B = "3" });
pairs.Add(new Pair { A = "1", B = "4" });
pairs.Add(new Pair { A = "2", B = "1" });
pairs.Add(new Pair { A = "2", B = "2" });
pairs.Add(new Pair { A = "2", B = "3" });
var grouping = pairs.GroupBy(x => x.A);
var combinations = grouping.SelectMany(x =>
grouping.Select(y =>
new { Group = x, Combination = y }));
You can do this,following the line of thinking of romoku and chrisC
//new list of lists to hold new information.
List<List<Descendants>> NewList = new List<List<Descendants>>();
foreach (var item in Data.Descendants.GroupBy(x => x.Attribute("v").Value))
{
NewList.Add(item.ToList());
}
For your new edit list of Strings this will do it
List<List<string>> NewList = new List<List<string>>();
foreach (var item in OriginalList.GroupBy(x => x))
{
NewList.Add(item.ToList());
}
I have a dict<string, list<string>>, say 3 keys in dict, the first key has 2 values, the secodn 3 values, the third key has 3 values. If I get a value from each value set, then I will have a combination of 2*3*3 = 18 sets
How to code in c#?
thanks
Edit
Sorry did not make it clear
I want something like this
say I have dict like this
{"1",new List<String>(){"a", "b"}},
{"2",new List<String>(){"c", "d", "e"}},
{"3", new List<string>() {"f", "g"}
I want output like this
acf, acg, adf, adg, aef, aeg
bcf, bcg, bdf, bdg, bef, beg
With Linq:
var dict = new Dictionary<String, List<String>>() {
{"1",new List<String>(){"a", "b"}},
{"2",new List<String>(){"c", "d", "e"}},
{"3",new List<String>(){"f", "g", "h"}},
};
var combis = from kv in dict
from val1 in kv.Value
from val2 in kv.Value
select string.Format("{0}{1}", val1, val2);
foreach (var combi in combis)
Console.WriteLine(combi);
demo: http://ideone.com/nm7mY
Quick & dirty but you may polish this method. The result list contains expected result:
Usage:
var dict = new Dictionary<String, List<String>>() {
{"1",new List<String>(){"a", "b"}},
{"2",new List<String>(){"c", "d", "e"}},
{"3",new List<String>(){"f", "g"}},
};
var collections = dict.Select(kvp => kvp.Value).ToArray();
var result = new List<string>();
GetNextProduct(collections, 0, String.Empty, result);
Method that produces the result:
private static void GetNextProduct(IEnumerable<string>[] collections, int collectionIndex, string currentProduct, IList<string> results)
{
var currentList = collections[collectionIndex];
bool isLast = collections.Length == collectionIndex + 1;
foreach (var s in currentList)
{
if (isLast) results.Add(currentProduct + s);
else GetNextProduct(collections, collectionIndex + 1, currentProduct + s, results);
}
}
I think you mean this?
Dictionary<string, int> dict = new Dictionary<string, int>
{
{ "Hello World", 1 },
{ "HelloWorld", 1 },
{ "Hello World", 1 },
};
foreach (var item in dict) // var is of type KeyValuePair<string, int>
Console.WriteLine(item.Key + ", " + item.Value);
Dictionary<string, List<int>> storage = new Dictionary<string, List<int>>();
storage.Add("key1", new List<int>() { 2, 7 });
storage.Add("key2", new List<int>() { 8, 4, 1});
storage.Add("key3", new List<int>() { 3, 9, 3 });
foreach (string key in storage.Keys)
{
//access to single storage...
List<int> subStorage = (List<int>)storage[key];
foreach (int item in subStorage)
{
//access to single value inside storage...
}
}
I would try something like the following if I was trying to read or edit the values in the lists:
Dictionary<int, List<string>> dict = new Dictionary<int, List<string>>();
var arrayOfValues = dict.Values.ToArray();
for (int i = 0; i < arrayOfValues.Length; i++)
{
for (int j = 0; j < arrayOfValues[i].Count; j++)
{
//read/edit arrayOfValues[i][j];
}
}
You do not need recursion since you know the dept of the "tree".
I am trying to create a dictionary from 2 lists where one list contains keys and one list contains values. I can do it using for loop but I am trying to find if there is a way of doing it using LINQ.
Sample code will be helpfull. Thanks!!!!
In .NET4 you could use the built-in Zip method to merge the two sequences, followed by a ToDictionary call:
var keys = new List<int> { 1, 2, 3 };
var values = new List<string> { "one", "two", "three" };
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
.ToDictionary(x => x.Key, x => x.Value);
List<string> keys = new List<string>();
List<string> values = new List<string>();
Dictionary<string, string> dict = keys.ToDictionary(x => x, x => values[keys.IndexOf(x)]);
This of course assumes that the length of each list is the same and that the keys are unique.
UPDATE: This answer is far more efficient and should be used for lists of non-trivial size.
You can include the index in a Select expression to make this efficient:
var a = new List<string>() { "A", "B", "C" };
var b = new List<string>() { "1", "2", "3" };
var c = a.Select((x, i) => new {key = x, value = b[i]}).ToDictionary(e => e.key, e => e.value );
foreach (var d in c)
Console.WriteLine(d.Key + " = " + d.Value);
Console.ReadKey();
var dic = keys.Zip(values, (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
You can use this code and working perfectly.
C# Code:
var keys = new List<string> { "Kalu", "Kishan", "Gourav" };
var values = new List<string> { "Singh", "Paneri", "Jain" };
Dictionary<string, string> dictionary = new Dictionary<string, string>();
for (int i = 0; i < keys.Count; i++)
{
dictionary.Add(keys[i].ToString(), values[i].ToString());
}
foreach (var data in dictionary)
{
Console.WriteLine("{0} {1}", data.Key, data.Value);
}
Console.ReadLine();
Output Screen: