Adding to a range of generic lists in c# - c#

Anyone know of a way to add a value to a range of generic lists in c#?
I'm currently building up a large List<List<int>> and the whole process is taking too long and I'm trying to avoid using foreach loops and nested foreach loops in order to shave some time off.
Lets say I had 600 rows in a generic list. For each of the first 200 rows, I'd like to add a "1". For the next 200, I'd like to add a "2". For the next 200, I'd like to add a "3".
The way I'm doing that now, I have to loop through it 600 times and add each one individually, whereas what I'd like to do is loop through it 3 times and add the entries in bulk.
The code I was hoping for would be something like:
List<List<int>> idList = GetFullList(); //list contains 600 rows
int[] newItems = {1, 3, 5};
int count = 0;
int amountToAmend = 200;
foreach (int i in newItems)
{
//List<int> newID = new List<int>();
//newID.Add(i);
(idList.GetRange(count, amountToAmend)).Add(i);
count += amountToAmend;
}
Obviously this doesn't work, but hopefully you can see the kind of thing I'm going for. In my application I'm currently needing to do tens of thousands of unnecessary loops, when often less than 10 could feasibly do the job if the code exists!
UPDATE: I'm not sure I've explained this well, so just to clarify, here are the results I'm looking for here
If I have a list with 6 rows like so:
[6,7,8]
[5,6,7]
[6,4,8]
[2,4,7]
[5,1,7]
[9,3,5]
i know that I'd like to add a 1 to the first 3 rows and a 2 to the next 3 rows, so they would become:
[6,7,8,1]
[5,6,7,1]
[6,4,8,1]
[2,4,7,2]
[5,1,7,2]
[9,3,5,2]
This is easy to do with foreach loops and is how I currently do it, but because of the sheer volume of data involved, I'm looking for ways to cut the time taken on specific functions. I'm not sure if a way exists tbh, but if anyone knows, then it'll be the good people of Stack Overflow :)

You may use Skip and Take methods from LINQ.
like idList.Skip(0).Take(200) it will give you first 200 items from your list, then you may update these items.
For update you may say:
int increment=2;
list.Select(intVal=> intVal+increment).ToList();

How about this:
foreach (int i in newItems)
{
foreach (var row in idList.Skip(count).Take(amountToAmend))
{
row.Add(i);
}
count += amountToAmend;
}
Or with a for-loop:
foreach (int i in newItems)
{
for (int j = 0; j < amountToAmend; j++)
{
idList[count + j].Add(i);
}
count += amountToAmend;
}

You want to have amountToAmend times each item in newItems ?
Like :
200 times 1
200 times 3
200 times 5
If so, you can try :
int amountToAmend = 200;
List<int> newItems = new List<int>(){ 1, 3, 5 };
<List<int>> idList = new List<List<int>>();
newItems.ForEach(i => idList.Add(new List<int>(Enumerable.Repeat(i, amountToAmend))));

List<List<int>> idList = GetFullList(); //list contains 600 rows
var iterator = idList.Begin();
int[] newItems = {1, 3, 5};
int count = 0;
int amountToAmend = 200;
foreach (var item in newItems)
{
iterator = iterator.AddItem(item);
iterator = iterator.MoveForward(amountToAmend);
}
public struct NestedListIterator<T>
{
public NestedListIterator(List<List<T>> lists, int listIndex, int itemIndex)
{
this.lists = lists;
this.ListIndex = listIndex;
this.ItemIndex = itemIndex;
}
public readonly int ListIndex;
public readonly int ItemIndex;
public readonly List<List<T>> lists;
public NestedListIterator<T> AddItem(T item)
{
var list = lists.ElementAtOrDefault(ListIndex);
if (list == null || list.Count < ItemIndex)
return this;//or throw new Exception(...)
list.Insert(ItemIndex, item);
return new NestedListIterator<T>(this.lists, this.ListIndex, this.ItemIndex + 1);
}
public NestedListIterator<T> MoveForward(List<List<T>> lists, int index)
{
//if (index < 0) throw new Exception(..)
var listIndex = this.ListIndex;
var itemIndex = this.ItemIndex + index;
for (; ; )
{
var list = lists.ElementAtOrDefault(ListIndex);
if (list == null)
return new NestedListIterator<T>(lists, listIndex, itemIndex);//or throw new Exception(...)
if (itemIndex <= list.Count)
return new NestedListIterator<T>(lists, listIndex, itemIndex);
itemIndex -= list.Count;
listIndex++;
}
}
public static int Compare(NestedListIterator<T> left, NestedListIterator<T> right)
{
var cmp = left.ListIndex.CompareTo(right.ListIndex);
if (cmp != 0)
return cmp;
return left.ItemIndex.CompareTo(right.ItemIndex);
}
public static bool operator <(NestedListIterator<T> left, NestedListIterator<T> right)
{
return Compare(left, right) < 0;
}
public static bool operator >(NestedListIterator<T> left, NestedListIterator<T> right)
{
return Compare(left, right) > 0;
}
}
public static class NestedListIteratorExtension
{
public static NestedListIterator<T> Begin<T>(this List<List<T>> lists)
{
return new NestedListIterator<T>(lists, 0, 0);
}
public static NestedListIterator<T> End<T>(this List<List<T>> lists)
{
return new NestedListIterator<T>(lists, lists.Count, 0);
}
}

There is no builtin function, although you cannot avoid looping(explicit or implicit) at all since you want to add a new element to every list.
You could combine List.GetRange with List.ForEach:
var newItems = new[] { 1, 2 };
int numPerGroup = (int)(idList.Count / newItems.Length);
for (int i = 0; i < newItems.Length; i++)
idList.GetRange(i * numPerGroup, numPerGroup)
.ForEach(l => l.Add(newItems[i]));
Note that above is not Linq and would work even in .NET 2.0
This is my old approach which was not what you needed:
You can use Linq and Enumerable.GroupBy to redistribute a flat list into nested lists:
int amountToAmend = 200;
// create sample data with 600 integers
List<int> flattened = Enumerable.Range(1, 600).ToList();
// group these 600 numbers into 3 nested lists with each 200 integers
List<List<int>> unflattened = flattened
.Select((i, index) => new { i, index })
.GroupBy(x => x.index / amountToAmend)
.Select(g => g.Select(x => x.i).ToList())
.ToList();
Here's the demo: http://ideone.com/LlEe2

Related

C# Algorithm for Combinations/Permutations of a defined range of integers

I am trying to generate a list of unique combinations/permutations of 3 allowable values for 20 different participants (each of the 20 participants can be assigned a value of either 1, 2, or 3).
An example of one combination would be an array on length 20 with all ones like such:
{ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }
...and everything possible all the way up to
{ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 }
where each value in the array can be 1, 2 or 3.
I am stuck on writing my GetAllCombinations() function and I looked at some articles on permutation, but everything I have found is just confusing me more. I am not even sure if permutation is what I need here
So far I have this:
public List<int[]> GetValidCombinations()
{
const int positions = 20;
int[] acceptableValues = new int[3] { 1, 2, 3 };
//DO I USE PERMUTATION HERE TO BUILD MY ARRAY LIST OF ALL POSSIBLE COMBINATIONS?
var allPossibleCombinations = GetAllCombinations(positions, acceptableValues);
List<int[]> validList = new List<int[]>();
foreach (var combination in allPossibleCombinations)
{
//omited for berevity but I would
//do some other validations here...
if (true)
{
validList.Add(combination);
}
}
return validList;
}
public List<int[]> GetAllCombinations(int positions, int[] acceptableValues)
{
//For now returning null because I
//don't know How the heck to do this...
return null;
}
I have looked at some examples of permutation and I tried to use something like this below, but it did not produce what I was looking for:
static IEnumerable<IEnumerable<T>>
GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(o => !t.Contains(o)),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
public void Test()
{
const int k = 20;
var n = new[] { 1, 2, 3 };
var combinations = GetPermutations(n, k);
//DOES NOT WORK FOR WHAT I NEED
}
Running Test() worked with k was 3 or less but returned nothing if k was greater then 3.
Try this:
public static List<int[]> GetAllCombinations(int position, int[] acceptableVaues)
{
List<int[]> result = new List<int[]>();
int[] parent = new int[] { };
result = AddAPosition(parent, acceptableVaues);
while(position > 1)
{
var tmpResult = new List<int[]>();
foreach(var _parent in result)
{
tmpResult.AddRange(AddAPosition(_parent, acceptableVaues));
}
position--;
result = tmpResult;
}
return result;
}
public static List<int[]> AddAPosition(int[] parent, int[] acceptableValues)
{
List<int[]> result = new List<int[]>();
for (int i = 0; i< acceptableValues.Length; i++)
{
var anArray = new int[parent.Length + 1];
for (int j = 0; j< parent.Length; j++)
{
anArray[j] = parent[j];
}
anArray[parent.Length] = acceptableValues[i];
result.Add(anArray);
}
return result;
}

Comparison of dynamic number of lists in C#

I have a variable number of List-objects, of which I need to compare the values at the same indexes. The number of entries in the list is given and does not vary.
For example:
4 Lists
each 4 entries
So what I'd be looking for in this example, is 4 bools, one per index.
Getting the indexes of unmatching entries would be fine, too.
Pseudo:
bool isFirstEqual = (list1[i] == list2[i] == list3[i] == list4[i]);
But I need to do this in a fashion that's applicable for a variable number of lists. I could have 6 Lists, but also 2.
I was thinking of doing something with LINQs .Except() but am not sure how to use it with a variable number of lists.
I am struggling to find the elegant solution that I'm sure is out there.
Any help on this is appreciated.
If i understand what you mean, something like this might work
public static bool IsEqual<T>(int index, params List<T>[] ary)
{
for (var i = 1; i < ary.Length; i++)
if (!EqualityComparer<T>.Default.Equals(ary[0][index], ary[i][index]))
return false;
return true;
}
Usage
var isSecondelemEqual = IsEqual(1, list1, list2, list3,...)
Update
Basically takes a variable list of lists, and assumes you want to check the index of each list against each other.
bool AreIndexesEqual(int index, List<List<int>> lists)
{
int match = lists[0][index];
foreach(List<int> list in lists.Skip(1))
{
if (list[index] != match)
{
return false;
}
}
return true;
}
Given for example:
List<List<int>> listOfLists = new List<List<int>>
{
new List<int> { 1, 100, 2},
new List<int> { 2, 100, 3},
new List<int> { 3, 100, 4},
new List<int> { 4, 100, 5},
};
So a List<> of List<int>
the totally unreadable LINQ expression that will return a List<bool> is something like:
List<bool> result = Enumerable.Range(0, listOfLists.Count != 0 ? listOfLists[0].Count : 0)
.Select(x => listOfLists.Count <= 1 ?
true :
listOfLists.Skip(1).All(y => y[x] == listOfLists[0][x])
).ToList();
Here I want to show you that if your first solution to any problem is LINQ, then perhaps now you will have two problems.
Now... What does this linq does? We have 4 List<int>, each one with 3 elements... So 3 rows of 4 columns. We want to calculate the result "by row", so the first thing is discover the number of rows, that is listOfLists[0].Count (we put a pre-check for the case that we have 0 rows). Now we generate an index (like a for), using Enumerable.Range(0, numberofrows), like for (int i = 0; i < numberofrows; i++). For each row we see if there are 0 or 1 columns (listOfLists.Count <= 1), then the result is true, otherwise we compare all the other columns y[x] with the first column listOfLists[0][x].
With a dual for cycle it becomes clearer probably:
var result2 = new List<bool>(listOfLists.Count != 0 ? listOfLists[0].Count : 0);
// Note the use of .Capacity here. It is listOfLists.Count != 0 ? listOfLists[0].Count : 0
for (int col = 0; col < result2.Capacity; col++)
{
if (listOfLists.Count <= 1)
{
result2.Add(true);
}
else
{
bool equal = true;
for (int row = 1; row < listOfLists.Count; row++)
{
if (listOfLists[row][col] != listOfLists[0][col])
{
equal = false;
break;
}
}
result2.Add(equal);
}
}
Note that both programs can be simplified: new int[0].All(x => something) == true, so .All() on empty IEnumerable<> is true. You can remove listOfLists.Count <= 1 ? true : and if (...) { ... } else up to the else keyword, keeping only the code inside the else:
var result2 = new List<bool>(listOfLists.Count != 0 ? listOfLists[0].Count : 0);
// Note the use of .Capacity here. It is listOfLists.Count != 0 ? listOfLists[0].Count : 0
for (int col = 0; col < result2.Capacity; col++)
{
bool equal = true;
for (int row = 1; row < listOfLists.Count; row++)
{
if (listOfLists[row][col] != listOfLists[0][col])
{
equal = false;
break;
}
}
result2.Add(equal);
}
Here you go
IEnumerable<bool> CompareAll<T>(IEnumerable<IEnumerable<T>> source)
{
var lists = source.ToList();
var firstList = lists[0];
var otherLists = lists.Skip(1).ToList();
foreach(var t1 in firstList)
{
yield return otherlists.All(tn => tn.Equals(t1));
}
}

algorithm for selecting N random elements from a List<T> in C# [duplicate]

This question already has answers here:
Randomize a List<T>
(28 answers)
Closed 6 years ago.
I need a quick algorithm to select 4 random elements from a generic list. For example, I'd like to get 4 random elements from a List and then based on some calculations if elements found not valid then it should again select next 4 random elements from the list.
You could do it like this
public static class Extensions
{
public static Dictionary<int, T> GetRandomElements<T>(this IList<T> list, int quantity)
{
var result = new Dictionary<int, T>();
if (list == null)
return result;
Random rnd = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < quantity; i++)
{
int idx = rnd.Next(0, list.Count);
result.Add(idx, list[idx]);
}
return result;
}
}
Then use the extension method like this:
List<string> list = new List<string>() { "a", "b", "c", "d", "e", "f", "g", "h" };
Dictionary<int, string> randomElements = list.GetRandomElements(3);
foreach (KeyValuePair<int, string> elem in randomElements)
{
Console.WriteLine($"index in original list: {elem.Key} value: {elem.Value}");
}
something like that:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
int n = 4;
var rand = new Random();
var randomObjects = new List<int>();
for (int i = 0; i<n; i++)
{
var index = rand.Next(list.Count);
randomObjects.Add(list[index]);
}
}
}
You can store indexes in some list to get non-repeated indexes:
List<T> GetRandomElements<T>(List<T> allElements, int randomCount = 4)
{
if (allElements.Count < randomCount)
{
return allElements;
}
List<int> indexes = new List<int>();
// use HashSet if performance is very critical and you need a lot of indexes
//HashSet<int> indexes = new HashSet<int>();
List<T> elements = new List<T>();
Random random = new Random();
while (indexes.Count < randomCount)
{
int index = random.Next(allElements.Count);
if (!indexes.Contains(index))
{
indexes.Add(index);
elements.Add(allElements[index]);
}
}
return elements;
}
Then you can do some calculation and call this method:
void Main(String[] args)
{
do
{
List<int> elements = GetRandomelements(yourElements);
//do some calculations
} while (some condition); // while result is not right
}
Suppose that the length of the List is N. Now suppose that you will put these 4 numbers in another List called out. Then you can loop through the List and the probability of the element you are on being chosen is
(4 - (out.Count)) / (N - currentIndex)
funcion (list)
(
loop i=0 i < 4
index = (int) length(list)*random(0 -> 1)
element[i] = list[index]
return element
)
while(check == false)
(
elements = funcion (list)
Do some calculation which returns check == false /true
)
This is the pseudo code, but i think you should of come up with this yourself.
Hope it helps:)
All the answers up to now have one fundamental flaw; you are asking for an algorithm that will generate a random combination of n elements and this combination, following some logic rules, will be valid or not. If its not, a new combination should be produced. Obviously, this new combination should be one that has never been produced before. All the proposed algorithms do not enforce this. If for example out of 1000000 possible combinations, only one is valid, you might waste a whole lot of resources until that particular unique combination is produced.
So, how to solve this? Well, the answer is simple, create all possible unique solutions, and then simply produce them in a random order. Caveat: I will suppose that the input stream has no repeating elements, if it does, then some combinations will not be unique.
First of all, lets write ourselves a handy immutable stack:
class ImmutableStack<T> : IEnumerable<T>
{
public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
private readonly T head;
private readonly ImmutableStack<T> tail;
public int Count { get; }
private ImmutableStack()
{
Count = 0;
}
private ImmutableStack(T head, ImmutableStack<T> tail)
{
this.head = head;
this.tail = tail;
Count = tail.Count + 1;
}
public T Peek()
{
if (this == Empty)
throw new InvalidOperationException("Can not peek a empty stack.");
return head;
}
public ImmutableStack<T> Pop()
{
if (this == Empty)
throw new InvalidOperationException("Can not pop a empty stack.");
return tail;
}
public ImmutableStack<T> Push(T item) => new ImmutableStack<T>(item, this);
public IEnumerator<T> GetEnumerator()
{
var current = this;
while (current != Empty)
{
yield return current.head;
current = current.tail;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
This will make our life easier while producing all combinations by recursion. Next, let's get the signature of our main method right:
public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinationsInRandomOrder<T>(
IEnumerable<T> data, int combinationLength)
Ok, that looks about right. Now let's implement this thing:
var allCombinations = GetAllPossibleCombinations(data, combinationLength).ToArray();
var rnd = new Random();
var producedIndexes = new HashSet<int>();
while (producedIndexes.Count < allCombinations.Length)
{
while (true)
{
var index = rnd.Next(allCombinations.Length);
if (!producedIndexes.Contains(index))
{
producedIndexes.Add(index);
yield return allCombinations[index];
break;
}
}
}
Ok, all we are doing here is producing random indexees, checking we haven't produced it yet (we use a HashSet<int> for this), and returning the combination at that index.
Simple, now we only need to take care of GetAllPossibleCombinations(data, combinationLength).
Thats easy, we'll use recursion. Our bail out condition is when our current combination is the specified length. Another caveat: I'm omitting argument validation throughout the whole code, things like checking for null or if the specified length is not bigger than the input length, etc. should be taken care of.
Just for the fun, I'll be using some minor C#7 syntax here: nested functions.
public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinations<T>(
IEnumerable<T> stream, int length)
{
return getAllCombinations(stream, ImmutableStack<T>.Empty);
IEnumerable<IEnumerable<T>> getAllCombinations<T>(IEnumerable<T> currentData, ImmutableStack<T> combination)
{
if (combination.Count == length)
yield return combination;
foreach (var d in currentData)
{
var newCombination = combination.Push(d);
foreach (var c in getAllCombinations(currentData.Except(new[] { d }), newCombination))
{
yield return c;
}
}
}
}
And there we go, now we can use this:
var data = "abc";
var random = GetAllPossibleCombinationsInRandomOrder(data, 2);
foreach (var r in random)
{
Console.WriteLine(string.Join("", r));
}
And sure enough, the output is:
bc
cb
ab
ac
ba
ca

Find values that appear in all lists (or arrays or collections)

Given the following:
List<List<int>> lists = new List<List<int>>();
lists.Add(new List<int>() { 1,2,3,4,5,6,7 });
lists.Add(new List<int>() { 1,2 });
lists.Add(new List<int>() { 1,2,3,4 });
lists.Add(new List<int>() { 1,2,5,6,7 });
What is the best/fastest way of identifying which numbers appear in all lists?
You can use the .net 3.5 .Intersect() extension method:-
List<int> a = new List<int>() { 1, 2, 3, 4, 5 };
List<int> b = new List<int>() { 0, 4, 8, 12 };
List<int> common = a.Intersect(b).ToList();
To do it for two lists one would use x.Intersect(y).
To do it for several we would want to do something like:
var intersection = lists.Aggregate((x, y) => x.Intersect(y));
But this won't work because the result of the lambda isn't List<int> and so it can't be fed back in. This might tempt us to try:
var intersection = lists.Aggregate((x, y) => x.Intersect(y).ToList());
But then this makes n-1 needless calls to ToList() which is relatively expensive. We can get around this with:
var intersection = lists.Aggregate(
(IEnumerable<int> x, IEnumerable<int> y) => x.Intersect(y));
Which applies the same logic, but in using explicit types in the lambda, we can feed the result of Intersect() back in without wasting time and memory creating a list each time, and so gives faster results.
If this came up a lot we can get further (slight) performance improvements by rolling our own rather than using Linq:
public static IEnumerable<T> IntersectAll<T>(this IEnumerable<IEnumerable<T>> source)
{
using(var en = source.GetEnumerator())
{
if(!en.MoveNext()) return Enumerable.Empty<T>();
var set = new HashSet<T>(en.Current);
while(en.MoveNext())
{
var newSet = new HashSet<T>();
foreach(T item in en.Current)
if(set.Remove(item))
newSet.Add(item);
set = newSet;
}
return set;
}
}
This assumes its for internal use only. If it could be called from another assembly it should have error checks, and perhaps should be defined so as to only perform the intersect operations on the first MoveNext() of the calling code:
public static IEnumerable<T> IntersectAll<T>(this IEnumerable<IEnumerable<T>> source)
{
if(source == null)
throw new ArgumentNullException("source");
return IntersectAllIterator(source);
}
public static IEnumerable<T> IntersectAllIterator<T>(IEnumerable<IEnumerable<T>> source)
{
using(var en = source.GetEnumerator())
{
if(en.MoveNext())
{
var set = new HashSet<T>(en.Current);
while(en.MoveNext())
{
var newSet = new HashSet<T>();
foreach(T item in en.Current)
if(set.Remove(item))
newSet.Add(item);
set = newSet;
}
foreach(T item in set)
yield return item;
}
}
}
(In these final two versions there's an opportunity to short-circuit if we end up emptying the set, but it only pays off if this happens relatively often, otherwise it's a nett loss).
Conversely, if these aren't concerns, and if we know that we're only ever going to want to do this with lists, we can optimise a bit further with the use of Count and indices:
public static IEnumerable<T> IntersectAll<T>(this List<List<T>> source)
{
if (source.Count == 0) return Enumerable.Empty<T>();
if (source.Count == 1) return source[0];
var set = new HashSet<T>(source[0]);
for(int i = 1; i != source.Count; ++i)
{
var newSet = new HashSet<T>();
var list = source[i];
for(int j = 0; j != list.Count; ++j)
{
T item = list[j];
if(set.Remove(item))
newSet.Add(item);
}
set = newSet;
}
return set;
}
And further if we know we're always going to want the results in a list, and we know that either we won't mutate the list, or it won't matter if the input list got mutated, we can optimise for the case of there being zero or one lists (but this costs more if we might ever not need the output in a list):
public static List<T> IntersectAll<T>(this List<List<T>> source)
{
if (source.Count == 0) return new List<T>(0);
if (source.Count == 1) return source[0];
var set = new HashSet<T>(source[0]);
for(int i = 1; i != source.Count; ++i)
{
var newSet = new HashSet<T>();
var list = source[i];
for(int j = 0; j != list.Count; ++j)
{
T item = list[j];
if(set.Remove(item))
newSet.Add(item);
}
set = newSet;
}
return new List<T>(set);
}
Again though, as well as making the method less widely-applicable, this has risks in terms of how it could be used, so is only appropriate for internal code were you can know either that you won't change either the input or the output after the fact, or that this won't matter.
Linq already offers Intersect and you can exploit Aggregate as well:
var result = lists.Aggregate((a, b) => a.Intersect(b).ToList());
If you don't trust the Intersect method or you just prefer to see what's going on, here's a snippet of code that should do the trick:
// Output goes here
List<int> output = new List<int>();
// Make sure lists are sorted
for (int i = 0; i < lists.Count; ++i) lists[i].Sort();
// Maintain array of indices so we can step through all the lists in parallel
int[] index = new int[lists.Count];
while(index[0] < lists[0].Count)
{
// Search for each value in the first list
int value = lists[0][index[0]];
// No. lists that value appears in, we want this to equal lists.Count
int count = 1;
// Search all the other lists for the value
for (int i = 1; i < lists.Count; ++i)
{
while (index[i] < lists[i].Count)
{
// Stop if we've passed the spot where value would have been
if (lists[i][index[i]] > value) break;
// Stop if we find value
if (lists[i][index[i]] == value)
{
++count;
break;
}
++index[i];
}
// If we reach the end of any list there can't be any more matches so end the search now
if (index[i] >= lists[i].Count) goto done;
}
// Store the value if we found it in all the lists
if (count == lists.Count) output.Add(value);
// Skip multiple occurrances of the same value
while (index[0] < lists[0].Count && lists[0][index[0]] == value) ++index[0];
}
done:
Edit:
I got bored and did some benchmarks on this vs. Jon Hanna's version. His is consistently faster, typically by around 50%. Mine wins by about the same margin if you happen to have presorted lists, though. Also you can gain a further 20% or so with unsafe optimisations. Just thought I'd share that.
You can also get it with SelectMany and Distinct:
List<int> result = lists
.SelectMany(x => x.Where(e => lists.All(l => l.Contains(e))))
.Distinct().ToList();
Edit:
List<int> result2 = lists.First().Where(e => lists.Skip(1).All(l => l.Contains(e)))
.ToList();
Edit 2:
List<int> result3 = lists
.Select(l => l.OrderBy(n => n).Take(lists.Min(x => x.Count()))).First()
.TakeWhile((n, index) => lists.Select(l => l.OrderBy(x => x)).Skip(1).All(l => l.ElementAt(index) == n))
.ToList();

Generate combinations of elements held in multiple list of strings in C#

I'm trying to automate the nested foreach provided that there is a Master List holding List of strings as items for the following scenario.
Here for example I have 5 list of strings held by a master list lstMaster
List<string> lst1 = new List<string> { "1", "2" };
List<string> lst2 = new List<string> { "-" };
List<string> lst3 = new List<string> { "Jan", "Feb" };
List<string> lst4 = new List<string> { "-" };
List<string> lst5 = new List<string> { "2014", "2015" };
List<List<string>> lstMaster = new List<List<string>> { lst1, lst2, lst3, lst4, lst5 };
List<string> lstRes = new List<string>();
foreach (var item1 in lst1)
{
foreach (var item2 in lst2)
{
foreach (var item3 in lst3)
{
foreach (var item4 in lst4)
{
foreach (var item5 in lst5)
{
lstRes.Add(item1 + item2 + item3 + item4 + item5);
}
}
}
}
}
I want to automate the below for loop regardless of the number of list items held by the master list lstMaster
Just do a cross-join with each successive list:
IEnumerable<string> lstRes = new List<string> {null};
foreach(var list in lstMaster)
{
// cross join the current result with each member of the next list
lstRes = lstRes.SelectMany(o => list.Select(s => o + s));
}
results:
List<String> (8 items)
------------------------
1-Jan-2014
1-Jan-2015
1-Feb-2014
1-Feb-2015
2-Jan-2014
2-Jan-2015
2-Feb-2014
2-Feb-2015
Notes:
Declaring lstRes as an IEnumerable<string> prevents the unnecessary creation of additional lists that will be thrown away
with each iteration
The instinctual null is used so that the first cross-join will have something to build on (with strings, null + s = s)
To make this truly dynamic you need two arrays of int loop variables (index and count):
int numLoops = lstMaster.Count;
int[] loopIndex = new int[numLoops];
int[] loopCnt = new int[numLoops];
Then you need the logic to iterate through all these loopIndexes.
Init to start value (optional)
for(int i = 0; i < numLoops; i++) loopIndex[i] = 0;
for(int i = 0; i < numLoops; i++) loopCnt[i] = lstMaster[i].Count;
Finally a big loop that works through all combinations.
bool finished = false;
while(!finished)
{
// access current element
string line = "";
for(int i = 0; i < numLoops; i++)
{
line += lstMaster[i][loopIndex[i]];
}
llstRes.Add(line);
int n = numLoops-1;
for(;;)
{
// increment innermost loop
loopIndex[n]++;
// if at Cnt: reset, increment outer loop
if(loopIndex[n] < loopCnt[n]) break;
loopIndex[n] = 0;
n--;
if(n < 0)
{
finished=true;
break;
}
}
}
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<IEnumerable<T>> lists)
{
IEnumerable<IEnumerable<T>> result = new List<IEnumerable<T>> { new List<T>() };
return lists.Aggregate(result, (current, list) => current.SelectMany(o => list.Select(s => o.Union(new[] { s }))));
}
var totalCombinations = 1;
foreach (var l in lstMaster)
{
totalCombinations *= l.Count == 0 ? 1 : l.Count;
}
var res = new string[totalCombinations];
for (int i = 0; i < lstMaster.Count; ++i)
{
var numOfEntries = totalCombinations / lstMaster[i].Count;
for (int j = 0; j < lstMaster[i].Count; ++j)
{
for (int k = numOfEntries * j; k < numOfEntries * (j + 1); ++k)
{
if (res[k] == null)
{
res[k] = lstMaster[i][j];
}
else
{
res[k] += lstMaster[i][j];
}
}
}
}
The algorithm starts from calculating how many combinations we need for all the sub lists.
When we know that we create a result array with exactly this number of entries. Then the algorithm iterates through all the sub lists, extract item from a sub list and calculates how many times the item should occur in the result and adds the item the specified number of times to the results. Moves to next item in the same list and adds to remaining fields (or as many as required if there is more than two items in the list). And it continues through all the sub lists and all the items.
One area though that needs improvement is when the list is empty. There is a risk of DivideByZeroException. I didn't add that. I'd prefer to focus on conveying the idea behind the calculations and didn't want to obfuscate it with additional checks.

Categories