Related
I have two IEnumerables:
IEnumerable<string> first = ...
IEnumerable<string> second = ...
I want to create a second IEnumerable<string> that is the concatenation of each element of each IEnumerable.
For example:
IEnumerable<string> first = new [] {"a", "b"};
IEnumerable<string> second = new [] {"c", "d"};
foreach (string one in first)
{
foreach (string two in second)
{
yield return string.Format("{0} {1}", one, two);
}
}
This would produce:
"a c"; "a d"; "b c"; "b d";
The problem is, sometimes one of the two IEnumerables is empty:
IEnumerable<string> first = new string[0];
IEnumerable<string> second = new [] {"c", "d"};
In this case, the nested foreach construct never reaches the yield return statement. When either IEnumerable is empty, I would like the result to just be the list of the non-empty IEnumerable.
How can I produce the combinations I am looking for?
EDIT:
In reality, I have three different IEnumerables I am trying to combine, so adding if conditions for every possible permutation of empty IEnumerable seems bad. If that's the only way, then I guess I'll have to do it that way.
You can simply check that first enumerable is not empty:
IEnumerable<string> first = new [] {"a", "b"};
IEnumerable<string> second = new [] {"c", "d"};
var firstList = first.ToList();
if (!firstList.Any()) {
return second;
}
foreach (string one in firstList)
{
foreach (string two in second)
{
yield return string.Format("{0} {1}", one, two);
}
}
To eliminate double IEnumerable evaluation in positive cases just convert first enumerable to list
Simply use Enumerable.DefaultIfEmpty() to enumerate collection even if there is no items.
IEnumerable<string> first = new string[0];
IEnumerable<string> second = new[] { "a", "b" };
IEnumerable<string> third = new[] { "c", null, "d" };
var permutations =
from one in first.DefaultIfEmpty()
from two in second.DefaultIfEmpty()
from three in third.DefaultIfEmpty()
select String.Join(" ", NotEmpty(one, two, three));
Note: I have used String.Join to join items which are not null or empty and method to select non-empty items to be joined (you can inline this code if you don't want to have a separate method):
private static IEnumerable<string> NotEmpty(params string[] items)
{
return items.Where(s => !String.IsNullOrEmpty(s));
}
Output for sample above is
[ "a c", "a", "a d", "b c", "b", "b d" ]
For two collections and foreach loops (though I would prefere LINQ as above):
IEnumerable<string> first = new[] { "a", "b" };
IEnumerable<string> second = new string[0];
foreach(var one in first.DefaultIfEmpty())
{
foreach(var two in second.DefaultIfEmpty())
yield return $"{one} {two}".Trim(); // with two items simple Trim() can be used
}
Output:
[ "a", "b" ]
Your current approach should work until any of the collections is empty. If this is the case you need some check in front:
if(!first.Any())
foreach(var e in second) yield return e;
else if(!second.Any())
foreach(var e in first) yield return e;
foreach (string one in first)
{
foreach (string two in second)
{
yield return string.Format("{0} {1}", one, two);
}
}
However you should consider making an immediate execution using ToList in front to avoid multiple iterations of the same collection.
Assuming you're output for case :
IEnumerable<string> first = new string[0];
IEnumerable<string> second = new [] {"c", "d"};
would be :
c
d
This would work :
var query = from x in first.Any() ? first : new [] { "" }
from y in second.Any() ? second : new[] { "" }
select x + y;
Less code , easier to maintain and debug !
Edit : If you have any other IEnumerable is just 1 extra line per IEnumerable ( includes the check )
var query = from x in first.Any() ? first : new [] { "" }
from y in second.Any() ? second : new[] { "" }
from z in third.Any() ? third : new[] { "" }
select x + y + z;
Edit 2 : you can just add the spaces at the end :
select (x + y + z).Aggregate(string.Empty, (c, i) => c + i + ' ');
If you have more than a couple lists, you can setup a recursive iterator. You'll want to be mindful of the stack, and I think the string concatenation is less than ideal, and passing lists of lists is rather clunky, but this should get you started.
using System;
using System.Collections.Generic;
using System.Linq;
namespace en
{
class Program
{
static void Main(string[] args)
{
// three sample lists, for demonstration purposes.
var a = new List<string>() { "a", "b", "c" };
var b = new List<string>() { "1", "2", "3" };
var c = new List<string>() { "i", "ii", "iii" };
// the function needs everything in one argument, so create a list of the lists.
var lists = new List<List<string>>() { a, b, c };
var en = DoStuff(lists).GetEnumerator();
while (en.MoveNext())
{
Console.WriteLine(en.Current);
}
}
// This is the internal function. I only made it private because the "prefix" variable
// is mostly for internal use, but there might be a use case for exposing that ...
private static IEnumerable<String> DoStuffRecursive(IEnumerable<String> prefix, IEnumerable<IEnumerable<String>> lists)
{
// start with a sanity check
if (object.ReferenceEquals(null, lists) || lists.Count() == 0)
{
yield return String.Empty;
}
// Figure out how far along iteration is
var len = lists.Count();
// down to one list. This is the exit point of the recursive function.
if (len == 1)
{
// Grab the final list from the parameter and iterate over the values.
// Create the final string to be returned here.
var currentList = lists.First();
foreach (var item in currentList)
{
var result = prefix.ToList();
result.Add(item);
yield return String.Join(" ", result);
}
}
else
{
// Split the parameter. Take the first list from the parameter and
// separate it from the remaining lists. Those will be handled
// in deeper calls.
var currentList = lists.First();
var remainingLists = lists.Skip(1);
foreach (var item in currentList)
{
var iterationPrefix = prefix.ToList();
iterationPrefix.Add(item);
// here's where the magic happens. You can't return a recursive function
// call, but you can return the results from a recursive function call.
// http://stackoverflow.com/a/2055944/1462295
foreach (var x in DoStuffRecursive(iterationPrefix, remainingLists))
{
yield return x;
}
}
}
}
// public function. Only difference from the private function is the prefix is implied.
public static IEnumerable<String> DoStuff(IEnumerable<IEnumerable<String>> lists)
{
return DoStuffRecursive(new List<String>(), lists);
}
}
}
console output:
a 1 i
a 1 ii
a 1 iii
a 2 i
a 2 ii
a 2 iii
a 3 i
a 3 ii
a 3 iii
b 1 i
b 1 ii
b 1 iii
b 2 i
b 2 ii
b 2 iii
b 3 i
b 3 ii
b 3 iii
c 1 i
c 1 ii
c 1 iii
c 2 i
c 2 ii
c 2 iii
c 3 i
c 3 ii
c 3 iii
I am looking to find the differences between two Lists of string arrays using the index 0 of the array as the primary key.
List<string[]> original = new List<string[]>();
List<string[]> web = new List<string[]>();
//define arrays for List 'original'
string[] original_a1 = new string[3]{"a","2","3"};
string[] original_a2 = new string[3]{"x","2","3"};
string[] original_a3 = new string[3]{"c","2","3"};
//define arrays for List 'web'
string[] web_a1 = new string[3]{"a","2","3"};
string[] web_a2 = new string[3]{"b","2","3"};
string[] web_a3 = new string[3]{"c","2","3"};
//populate Lists
original.Add(original_a1);
original.Add(original_a2);
original.Add(original_a3);
web.Add(web_a1);
web.Add(web_a2);
web.Add(web_a3);
My goal is to find what is in List 'original' but NOT in 'web' by using index 0 as the primary key
This is what I tried.
List<string> differences = new List<string>(); //differences go in here
string tempDiff = ""; // I use this to try and avoid duplicate entries but its not working
for(int i = 0; i < original.Count; i++){
for(int j = 0; j< web.Count; j++){
if(!(original[i][0].Equals(web[j][0]))){
tempDiff = original[i][0];
}
}
differences.Add(tempDiff);
}
OUTPUT:
foreach(string x in differences){
Console.WriteLine("SIZE " + differences.Count);
Console.WriteLine(x);
ConSole.ReadLine();
}
SIZE 3
SIZE 3
x
SIZE 3
x
Why is it reporting the mismatch 3 times instead of once?
Using linq you can just go:
var differences = orignal.Except(web).ToList();
Reference here
This will give you the values that are in original, that don't exist in web
Sorry didn't read your question properly, to answer your question:
You have a nested for-loop. So for each value of original (3) it will loop through all values of web (3), which is 9 loops total.
In 3 cases it doesn't match and therefore outputs 3 times.
I think this is what you want. I use Linq to grab the primary keys, and then I use Except to do original - web. By the way, you can use == instead of Equals with strings in C# because C# does a value comparison as opposed to a reference comparison.
List<string[]> original = new List<string[]>
{
new string[3] { "a", "2", "3" },
new string[3] { "x", "2", "3" },
new string[3] { "c", "2", "3" }
};
List<string[]> web = new List<string[]>
{
new string[3] { "a", "2", "3" },
new string[3] { "b", "2", "3" },
new string[3] { "c", "2", "3" }
};
var originalPrimaryKeys = original.Select(o => o[0]);
var webPrimaryKeys = web.Select(o => o[0]);
List<string> differences = originalPrimaryKeys.Except(webPrimaryKeys).ToList();
Console.WriteLine("The number of differences is {0}", differences.Count);
foreach (string diff in differences)
{
Console.WriteLine(diff);
}
And here it is without Linq:
var differences = new List<string>();
for (int i = 0; i < original.Count; i++)
{
bool found = false;
for (int j = 0; j < web.Count; j++)
{
if (original[i][0] == web[j][0])
{
found = true;
}
}
if (!found)
{
differences.Add(original[i][0]);
}
}
To answer your question: It is a nested for loop as stated in JanR's answer. This approach will make you reiterate to your web count 9 times, thus listing your mismatched key three times.
What could be a better way to do is this:
//Check for originals not introduced in web.
if(original.Count > web.Count)
{
for(int y = web.Count; y < original.Count; y++)
{
differences.Add(original[y][0]);
}
}
//Check if Web has value, if not, everything else is done on the first for loop
if(web.Count > 0)
{
for(int i = 0; i < original.Count; i++)
{
if(!original[i][0].Equals(web[i][0]))
differences.Add(original[i][0]);
}
}
Also, the output is in a for loop, when you just need one result, the length of the mismatch. You can do that without a loop.
Console.WriteLine("SIZE " + differences.Count);
This is, of course to make it kinda simpler if you're not used to using LINQ statements, but if you can do so with LINQ, then by all means, use LINQ as it's more efficient.
You can get the difference by using Except extension method like this:
var originalDic = original.ToDictionary(arr => arr.First());
var webDic = web.ToDictionary(arr => arr.First());
var differences =
originalDic
.Except(webDic, kvp => kvp.Key)
.Select(kvp => kvp.Value)
.ToList();
The trick here is to first convert your original and web lists into a Dictionary using the first element of each array as key and then perform Except.
I have searched without success to a similar situation as follows.
I have two lists, list A and list B.
List A is composed of 10 objects created from ClassA which contains only strings.
List B is composed of 100 objects created from ClassB which only contains decimals.
List A is the header information.
List B is the data information.
The relationship between the two lists is:
Row 1 of list A corresponds to rows 1-10 of list B.
Row 2 of list A corresponds to rows 11-20 of list B.
Row 3 of list A corresponds to rows 21-30 of list B.
etc.........
How can I combine these two lists so that when I display them on the console the user will see a header row followed immediately by the corresponding 10 data rows.
I apologize if this has been answered before.
Ok, that should work. Let me know in case I got anything wrong.
List<ClassA> listA = GetListA()// ...
List<ClassB> listB = GetListA()// ...
if(listB.Count % listA.Count != 0)
throw new Exception("Unable to match listA to listB");
var datasPerHeader = listB.Count / listA.Count;
for(int i = 0; i < listA.Count;i++)
{
ClassA header = listA[i];
IEnumerable<ListB> datas = listB.Skip(datasPerHeader*i).Take(datasPerHeader);
Console.WriteLine(header.ToString());
foreach(var data in datas)
{
Console.WriteLine("\t{0}", data.ToString());
}
}
Here is some code that should fulfill your request - I am going to find a link for the partition extension as I can't find it in my code anymore:
void Main()
{
List<string> strings = Enumerable.Range(1,10).Select(x=>x.ToString()).ToList();
List<decimal> decimals = Enumerable.Range(1,100).Select(x=>(Decimal)x).ToList();
var detailsRows = decimals.Partition(10)
.Select((details, row) => new {HeaderRow = row, DetailsRows = details});
var headerRows = strings.Select((header, row) => new {HeaderRow = row, Header = header});
var final = headerRows.Join(detailsRows, x=>x.HeaderRow, x=>x.HeaderRow, (header, details) => new {Header = header.Header, Details = details.DetailsRows});
}
public static class Extensions
{
public static IEnumerable<List<T>> Partition<T>(this IEnumerable<T> source, Int32 size)
{
for (int i = 0; i < Math.Ceiling(source.Count() / (Double)size); i++)
yield return new List<T>(source.Skip(size * i).Take(size));
}
}
That Partition method is the one that does the grunt work...
And here is the link to the article - LINK
EDIT 2
Here is better code for the Main() method... Rushed to answer and forgot brain:
void Main()
{
List<string> strings = Enumerable.Range(1,10).Select(x=>x.ToString()).ToList();
List<decimal> decimals = Enumerable.Range(1,100).Select(x=>(Decimal)x).ToList();
var detailsRows = decimals.Partition(10);
var headerRows = strings; //just renamed for clarity from other code
var final = headerRows.Zip(detailsRows, (header, details) => new {Header = header, Details = details});
}
This should be pretty straight forward unless I'm missing something.
var grouped = ListA.Select((value, index) =>
new {
ListAItem = value,
ListBItems = ListB.Skip(index * 10).Take(10)
})
.ToList();
Returns back an anonymous type you can loop through.
foreach (var group in grouped)
{
Console.WriteLine("List A: {0}", group.Name);
foreach (var listBItem in group.ListBItems)
{
Console.WriteLine("List B: {0}", listBItem.Name);
{
}
The easiest way may be something like this:
var listA = new List<string>() { "A", "B", "C", ... }
var listB = new List<decimal>() { 1m, 2m, 3m, ... }
double ratio = ((double)listA.Count) / listB.Count;
var results =
from i in Enumerable.Range(0, listB.Count)
select new { A = listA[(int)Math.Truncate(i * ratio)], B = listB[i] };
Or in fluent syntax:
double ratio = ((double)listA.Count) / listB.Count;
var results = Enumerable.Range(0, listB.Count)
.Select(i => new { A = listA[(int)Math.Truncate(i * ratio)], B = listB[i] });
Of course if you know you will always have 10 items in listB for each item in listA, you can simplify this to:
var results =
from i in Enumerable.Range(0, listB.Count)
select new { A = listA[i / 10], B = listB[i] };
Or in fluent syntax:
var results = Enumerable.Range(0, listB.Count)
.Select(i => new { A = listA[i / 10], B = listB[i] });
This will return a result set like
{ { "A", 1 },
{ "A", 2 },
{ "A", 3 }
..,
{ "A", 10 },
{ "B", 11 },
{ "B", 12 },
{ "B", 13 },
...
{ "B", 20 },
{ "C", 21 },
...
{ "J", 100 }
}
in linq, is it possible to combine many lists (of the same type), such that two lists,
list 1 = {a,b,c} and list 2 = {x,y,z}
turns into {[1,a] , [1,b] , [1,c] , [2,x] , [2,y] , [2,z] }
where [] represents a pair containing a "list identifier"
The problem is from having decks of arbitrary cards, where each deck is a list in a collection of lists.
I'm trying to create a query such that I can select only cards in a certain deck, or cards similar to 2 or more decks.
This is probably a duplicate question, but I don't know how to search for the question further then I already have.
List<List<int>> lists;
var combined = lists.Select((l, idx) => new { List = l, Idx = idx })
.SelectMany(p => p.List.Select(i => Tuple.Create(p.Idx + 1, i)));
var list1 = new List<string>() {a,b,c};
var list2 = new List<string>() {x,y,z};
var combined = list1.Select(x => new { id = 1, v = x }).Concat(list2.Select(x => new { id = 2, v = x }));
Normally I'd suggest Enumerable.Zip for combining multiple lists, however you seem to actually want to concatenate multiple lists with a list counter.
public IEnumerable<Tuple<int,T>> Combine<T>(params IEnumerable<T>[] lists) {
return lists.Select((x,i) => x.Select(y => Tuple.Create(i+1,y))).SelectMany (l =>l);
}
UPDATE
Completely missed that SelectMany has the index option so the above code can be written as
public IEnumerable<Tuple<int,T>> Combine<T>(params IEnumerable<T>[] lists) {
return lists.SelectMany((x,i) => x.Select(y => Tuple.Create(i+1,y)));
}
Then you can do
var list1 = new List<string> { "a", "b", "c" };
var list2 = new List<string> { "x", "y", "z" };
var combined = Combine(list1,list2);
Combined will be enumerable of tuples, with Item1 being the list index identifier (starting at 1) and Item2 being the value.
This method will handle multiple lists so you could just as easily call it with:
var list3 = new List<string> { "f", "g" };
var combined = Combine(list1,list2,list3);
You can merge the lists like:
var first = new List<string> {"a","b","c"};
var second = new List<string> {"x","y","z"};
var merged = first.Select(item => new { ListIndex = 1, Value = item}).ToList();
merged.AddRange(second.Select(item => new { ListIndex = 2, Value = item});
//or use concat
var merged = first.Select(item => new { ListIndex = 1, Value = item});
.Concat(second.Select(item => new { ListIndex = 2, Value = item});
Alternatively if you have the sources in something like:
List<List<string>> lists = new List<List<string>>
{
new List<string> {"a","b","c"},
new List<string> {"x","y","z"}
};
you can do:
var merged = lists.SelectMany((item, index) =>
item.Select(s => new { ListIndex = index, Value = s}));
Note that this will produce a 0-based list, so if you really need a 1-base list, just do ListIndex = index +1.
Also, if you will use this a lot, I would create it as an specific entity, something like
struct ListIdentValue
{
public int ListIndex {get; private set;}
public string Value {get; private set;}
public ListIdentValue(int listIndex, string value) {...}
}
Try using Concat
new[] {'a','b','c'}
.Select(v=>new Tuple<int,char>(1, v))
.Concat(
new[] {'x','y','z'}.Select(v=>new Tuple<int,char>(2, v))
)
string[] a = { "a", "b", "c" };
string[] b = { "x", "z", "y" };
var t =
(
from ai in a
select new { listNo = 1, Item = ai }
).Union
(
from bi in b
select new { listNo = 2, Item = bi }
);
or
var t =
(
from ai in a
select new object[] { 1, ai }
).Union
(
from bi in b
select new object[] { 2, bi }
);
I have a problem where I have to find multiple combinations of subsets within nested hashsets. Basically I have a "master" nested HashSet, and from a collection of "possible" nested HashSets I have to programmatically find the "possibles" that could be simultaneous subsets of the "master".
Lets say I have the following:
var master = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "A", "B", "C"}),
new HashSet<string>( new string[] { "D", "E"}),
new HashSet<string>( new string[] { "F"})
}
);
var possible1 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "A", "B", "C"}),
new HashSet<string>( new string[] { "F"})
}
);
var possible2 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "D", "E"})
}
);
var possible3 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "F"})
}
);
var possible4 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "X", "Y", "Z"})
}
);
var possible5 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "A", "B" }),
new HashSet<string>( new string[] { "D", "E"})
}
);
The output I should get from my algorithm should be as follows:
All possible combination subsets:
possible1 and possible2
possible3 and possible5
possible2 and possible3
possible1
possible2
possible3
possible5
I'm trying to figure out the best way to approach this. There is, of course, the brute force option, but I'm trying to avoid that if I can.
I just hope my question was clear enough.
EDIT
To further elaborate on what constitutes a subset, here are some examples, given the master {{"A","B","C"},{"C","D","E",F"},{"X","Y","Z"}} :
{{"A","B"}{"C","D"}} would be a subset of
{{"A","B","C"},{"X","Y"}} would be a subset
{{"A","B"},{"A","B"}} would NOT be a subset
{{"A","B","C","D"}} would NOT be a subset
{{"A","B","C"},{"C","D","X"}} would NOT be a subset
Basically each child set needs to be a subset of a corresponding child in the master.
Use bruteforce:
public static int IsCsInMaster(HashSet<string> childSubset, List<HashSet<string>> master, int startIndex)
{
for (int i = startIndex; i < master.Count; i++)
if (childSubset.IsSubsetOf(master[i])) return i;
return -1;
}
public static bool IsChildInMaster(List<HashSet<string>> child, List<HashSet<string>> master)
{
foreach (var childSubset in child) if (IsCsInMaster(childSubset, master, 0) == -1) return false;
return true;
}
public static bool IsChildInMasterMulti(List<HashSet<string>> child, List<HashSet<string>> master)
{
Dictionary<int, int> subsetChecker = new Dictionary<int, int>();
List<IEnumerable<int>> multiMatches = new List<IEnumerable<int>>();
int subsetIndex;
// Check for matching subsets.
for (int i = 0; i < child.Count; i++)
{
subsetIndex = 0;
List<int> indexes = new List<int>();
while ((subsetIndex = IsCsInMaster(child[i], master, subsetIndex)) != -1)
{
indexes.Add(subsetIndex++);
}
if (indexes.Count == 1)
{
subsetIndex = indexes[0];
if (subsetChecker.ContainsKey(subsetIndex)) return false;
else subsetChecker[subsetIndex] = subsetIndex;
}
else
{
multiMatches.Add(indexes);
}
}
/*** Check for multi-matching subsets. ***/ //got lazy ;)
var union = multiMatches.Aggregate((aggr, indexes) => aggr.Union(indexes));
// Filter the union so only unmatched subset indexes remain.
List<int> filteredUion = new List<int>();
foreach (int index in union)
{
if (!subsetChecker.ContainsKey(index)) filteredUion.Add(index);
}
return (filteredUion.Count >= multiMatches.Count);
}
And in code:
IsChildInMasterMulti(possible2, master)
The code does not handle the {{"A","B"},{"A","B"}} case, though. That is a LOT more difficult (flagging used subsets in master, maybe even individual elements - recursively).
Edit2: The third method handles the {{"A","B"},{"A","B"}} case as well (and more).
Use the simplest solution possible.
Keep in mind that if someone else has to look at your code they should be able to understand what it's doing with as little effort as possible. I already found it hard to understand from your description what you want to do and I haven't had to read code yet.
If you find that it's too slow after it's working optimize it then.
If possible write unit tests. Unit tests will ensure that your optimized solution is also working correctly and will help others ensure their changes don't break anything.