C# How to Compare List<string[]> item to the next? - c#

I have a List<string[]>, and I would like to compare the first array from the list to the next, then print the parts that are equal in both string arrays.
For instance:
public static void Main()
{
List<string[]> list = new List<string[]>();
string[] a = {"some", "random", "string"};
string[] b = {"some", "other", "random", "string"};
list.Add(a);
list.Add(b);
string[] difference = Compare(list);
}
public static string[] Compare (List<string[]> A)
{
//do something here
}
The end goal is to loop it to compare each string array to every other string array in the list.
Thanks in advance.

1. Find intersections of pairs of arrays in the list
private static IEnumerable<(string,string[])> Compare(List<(string Name,string[] Words)> lists)
{
for(int i = 0; i < lists.Count - 1; i++) {
var a = lists[i];
var b = lists[i + 1];
yield return ($"{a.Name}<->{b.Name}", a.Words.Intersect(b.Words).ToArray());
}
}
Test
Code
List<(string, string[])> list = new List<(string, string[])>();
string[] a = {"some", "random", "string"};
string[] b = {"some", "other", "random", "string"};
string[] c = {"some", "other2", "random", "string2"};
list.Add(("a", a));
list.Add(("b", b));
list.Add(("c", c));
foreach( var pair in Compare(list) )
Console.WriteLine($"{pair.Item1}: {string.Join(", ", pair.Item2)}");
Output
// .NETCoreApp,Version=v3.0
a<->b: some, random, string
b<->c: some, random
2. Find words in all arrays
private static string[] InAll(List<string[]> lists)
{
var inAll = new List<string>();
foreach(var list in lists ) {
foreach(var word in list) {
if(lists.All(l => l.Contains(word))) {
inAll.Add(word);
}
}
}
return inAll.Distinct().ToArray();
}
Test
Code
public static void Main(string[] args)
{
List<string[]> list = new List<string[]>();
string[] a = {"some", "random", "string"};
string[] b = {"some", "other", "random", "string"};
string[] c = {"some", "other2", "random", "string2"};
list.Add(a);
list.Add(b);
list.Add(c);
foreach( var inAll in InAll(list) ) Console.WriteLine(inAll);
}
Output
// .NETCoreApp,Version=v3.0
some
random

Let's implement (in general case) it with a help of Linq:
using System.Linq;
...
private static IEnumerable<T[]> Differences<T>(IEnumerable<IEnumerable<T>> source,
IEqualityComparer<T> comparer = null) {
if (null == source)
throw new ArgumentNullException(nameof(source));
if (null == comparer)
comparer = EqualityComparer<T>.Default;
if (null == comparer)
throw new ArgumentNullException(nameof(comparer),
$"No default comparer for {typeof(T).Name}");
Dictionary<T, int> prior = null;
foreach (var line in source) {
Dictionary<T, int> current = line
.GroupBy(item => item, comparer)
.ToDictionary(chunk => chunk.Key, chunk => chunk.Count(), comparer);
if (null != prior)
yield return current
.Where(item => prior.ContainsKey(item.Key))
.SelectMany(item => Enumerable
.Repeat(item.Key, Math.Min(item.Value, prior[item.Key])))
.ToArray();
prior = current;
}
}
Demo:
List<string[]> list = new List<string[]>() {
new [] { "some", "random", "string" },
new [] { "some", "other", "random", "string" },
new [] { "some", "some", "some" },
new [] { "some", "some", "other" },
new [] { "some", "other", "some" },
};
string demo = string.Join(Environment.NewLine, Differences(list)
.Select(line => string.Join(", ", line)));
Console.Write(demo);
Outcome:
some, random, string // 1st and 2nd strings
some // 2nd and 3d strings
some, some // 3d and 4th strings
some, some, other // 4th and 5th strings
If you want the 1st line only, add .FirstOrDefault():
string demo = Differences(list)
.Select(line => string.Join(", ", line))
.FirstOrDefault();
Console.Write(demo);
Outcome:
some, random, string
Finally, if you want to intersect all items (common items within all lines):
private static IEnumerable<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> source,
IEqualityComparer<T> comparer = null) {
if (null == source)
throw new ArgumentNullException(nameof(source));
if (null == comparer)
comparer = EqualityComparer<T>.Default;
if (null == comparer)
throw new ArgumentNullException(nameof(comparer),
$"No default comparer for {typeof(T).Name}");
Dictionary<T, int> prior = null;
foreach (var line in source) {
Dictionary<T, int> current = line
.GroupBy(item => item, comparer)
.ToDictionary(chunk => chunk.Key, chunk => chunk.Count(), comparer);
if (null != prior)
prior = current
.Where(item => prior.ContainsKey(item.Key))
.ToDictionary(item => item.Key, item => Math.Min(item.Value, prior[item.Key]));
else
prior = current;
}
return (prior ?? new Dictionary<T, int>())
.SelectMany(item => Enumerable.Repeat(item.Key, item.Value));
}

For an arbitrary amount of lists to be compared you could write something like this:
public static string[] Compare (List<string[]> lists)
{
var temp = lists.First().ToList();
foreach(var l in lists)
{
temp = temp.Intersect(l).ToList();
}
return temp.ToArray();
}
This would result in
some
random
string
Of course you would add error handling in case of an empty list and so on...

public static string[] Compare(List<string[]> A)
{
string result = string.Empty;
foreach(string s in A[0])
{
if(A[1].Contains(s))
{
if(result==string.Empty)
{
result += s;
}
else
{
result += "," + s;
}
}
}
return result.Split(',');
}

It seems like you simply need the intersection of all the arrays:
private static string[] GetCommonElements(List<string[]> list)
{
if (!list.Any())
{
return Array.Empty<string>();
}
IEnumerable<string> result = list[0];
foreach (string[] collection in list.Skip(1))
{
result = result.Intersect(collection);
}
return result.ToArray();
}

Personally, I tried to solve the problem using linq, and I hope the code below can answer to your question .
Result :
public static IEnumerable<string> Compare(List<string[]> items)
{
var liste = new List<string>() ;
AddingElements(items, liste);
return liste.Distinct();
}
private static void AddingElements(List<string[]> items, List<string> liste)
{
items.Skip(1).ToList().ForEach((e) =>
{
liste.AddRange(e.Difference(items.First()));
});
}
public static string[] Difference(this string[] sourceArray, string[] stringArray)
{
return sourceArray.Where(e => stringArray.Contains(e))
.ToArray();
}

Related

Trying to compare two lists c# - Should work?

I have a method that looks like this:
GetDrawing(Dictionary<string, List<string>> AllDrawings, Dictionary<string, bool> ImportData, string[] ItemsToCompare)
Firstly, the method creates a new List<string> with all the items from ImportData<string, bool> that have the value true and can be found in string[] ItemsToCompare
Secondly, I would like to compare the new List<string> with the List from AllDrawings<string, List<string>>. The method is in the end supposed to return a string with the key from AllDrawings<string>, List<String>> where the two lists match.
I have now spent a lot of hours trying to figure this out myself and also tried every answer I could find to similar questions here on Stackoverflow but with no luck.
Below is the full code for my method. As mentioned above I've tried a lot of different methods to compare the lists but the one following below is the latest try.
public static string GetDrawing(Dictionary<string, List<string>> AllDrawings, Dictionary<string, bool> ImportData, string[] ItemsToCompare)
{
string FinalDrawing = "";
try
{
List<string> AllCorrect = new List<string>();
foreach (var item in ImportData)
{
if (item.Value == true && ItemsToCompare.Contains(item.Key))
AllCorrect.Add(item.Key);
}
AllCorrect.Sort();
foreach (var DrawItem in AllDrawings)
{
DrawItem.Value.Sort();
var match = AllCorrect.SequenceEqual(DrawItem.Value);
if (match == true)
{
FinalDrawing = DrawItem.Key;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return FinalDrawing;
}
My problem is that the return value from var match = AllCorrect.SequenceEqual(DrawItem.Value); is false and therefore the FinalDrawing is never set.
All answers are very appreciated.
Thanks in advance!
Ok.. I already said that in comments, but just to make sure that you don't get yelled at too much for not-using-linq and such:
your program seems correct up to the point you told us.
Here's a simple test. I provided some stub data that cover all things that you seem to check:
only true things from importedata
only things that are listed in itemstocompare
input lists are not sorted
-> http://rextester.com/HAE73942
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
public class Program
{
// your original function, nothing changed
public static string GetDrawing(Dictionary<string, List<string>> AllDrawings, Dictionary<string, bool> ImportData, string[] ItemsToCompare)
{
string FinalDrawing = "";
try
{
List<string> AllCorrect = new List<string>();
foreach (var item in ImportData)
{
if (item.Value == true && ItemsToCompare.Contains(item.Key))
AllCorrect.Add(item.Key);
}
AllCorrect.Sort();
foreach (var DrawItem in AllDrawings)
{
DrawItem.Value.Sort();
var match = AllCorrect.SequenceEqual(DrawItem.Value);
if (match == true)
{
FinalDrawing = DrawItem.Key;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return FinalDrawing;
}
public static void Main(string[] args)
{
var allDrawings = new Dictionary<string, List<string>>();
allDrawings.Add("aaa", new List<string>{ "a03", "a01", "a02" }); // originally unsorted
allDrawings.Add("bbb", new List<string>{ "b03", "b01", "b02" }); // originally unsorted
allDrawings.Add("ccc", new List<string>{ "c03", "c01", "c02" }); // originally unsorted
var import = new Dictionary<string, bool>();
import.Add("b01", false); // falsey
import.Add("a05", true); // not in comparison
import.Add("a03", true);
import.Add("c01", false); // falsey
import.Add("a02", true);
import.Add("a04", true); // not in comparison
import.Add("a01", true);
var toCompare = new string[9];
toCompare[0]="a01"; toCompare[1]="a02"; toCompare[2]="a03";
toCompare[3]="b01"; toCompare[4]="b02"; toCompare[5]="b03";
toCompare[6]="c01"; toCompare[7]="c02"; toCompare[8]="c03";
var result = GetDrawing(allDrawings, import, toCompare);
Console.WriteLine("Result: " + result);
}
}
it works fine and prints aaa as it should.
This means that you had to have overlooked something in the input data. Maybe some strings are uppercase/lowercase? Maybe some strings have whitespaces inside and others not?
This code :
List<string> AllCorrect = new List<string>();
foreach (var item in ImportData)
{
if (item.Value == true && ItemsToCompare.Contains(item.Key))
AllCorrect.Add(item.Key);
}
AllCorrect.Sort();
Can be reduced to :
List<string> AllCorect = ImportData.Where(vp =>
ItemsToCompare.Contains(vp.Key) && vp.Value).Select(vp => vp.Key).OrderBy(vp => vp).ToList();
To solve the second problem you can do :
return AllDrawings.First(l => l.Value.OrderBy(l2 => l2).SequenceEqual(AllCorect)).Key;
P.S.
If First() always throws an exception then it suggests that problem lays in how this lists are filled with values and this is a different question.
Example :
public static string GetDrawing(Dictionary<string, List<string>> AllDrawings, Dictionary<string, bool> ImportData, string[] ItemsToCompare)
{
List<string> AllCorect = ImportData.Where(vp =>
ItemsToCompare.Contains(vp.Key) && vp.Value).Select(vp => vp.Key).OrderBy(vp => vp).ToList();
return AllDrawings.First(l => l.Value.OrderBy(l2 => l2).SequenceEqual(AllCorect)).Key;
}
static void Main(string[] args)
{
List<string> list1 = new List<string>() { "one", "two", "three" };
List<string> list2 = new List<string>() { "five", "six", "seven" };
Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>()
{
{"first", list1}, {"second", list2}
};
string[] itemsToCompare = { "one", "two", "three" };
var dict2 = new Dictionary<string, bool>()
{
{"one", true},
{"two", true},
{"three", true}
};
var result = GetDrawing(dict, dict2, itemsToCompare);
Console.WriteLine(result);
}
Output : first
If the strings really match also for casing your code would be correct. I propose you inspect your string sequences - create an readable string and add appropriate break points. Also try to sort case-insensitive if that is what is missing here
AllCorrect.Sort(StringComparer.InvariantCultureIgnoreCase);
var AllCorrectInfo = string.Join(", ", AllCorrect.ToArray());
foreach (var DrawItem in AllDrawings)
{
DrawItem.Value.Sort();
var DrawItemInfo = string.Join(", ", DrawItem.Value.ToArray());
var match = AllCorrect.SequenceEqual(DrawItem.Value, StringComparer.InvariantCultureIgnoreCase);
if (match == true)
{
FinalDrawing = DrawItem.Key;
}
}

How to select last value from each run of similar items?

I have a list. I'd like to take the last value from each run of similar elements.
What do I mean? Let me give a simple example. Given the list of words
['golf', 'hip', 'hop', 'hotel', 'grass', 'world', 'wee']
And the similarity function 'starting with the same letter', the function would return the shorter list
['golf', 'hotel', 'grass', 'wee']
Why? The original list has a 1-run of G words, a 3-run of H words, a 1-run of G words, and a 2-run of W words. The function returns the last word from each run.
How can I do this?
Hypothetical C# syntax (in reality I'm working with customer objects but I wanted to share something you could run and test yourself)
> var words = new List<string>{"golf", "hip", "hop", "hotel", "grass", "world", "wee"};
> words.LastDistinct(x => x[0])
["golf", "hotel", "grass", "wee"]
Edit: I tried .GroupBy(x => x[0]).Select(g => g.Last()) but that gives ['grass',
'hotel', 'wee'] which is not what I want. Read the example carefully.
Edit. Another example.
['apples', 'armies', 'black', 'beer', 'bastion', 'cat', 'cart', 'able', 'art', 'bark']
Here there are 5 runs (a run of A's, a run of B's, a run of C's, a new run of A's, a new run of B's). The last word from each run would be:
['armies', 'bastion', 'cart', 'art', 'bark']
The important thing to understand is that each run is independent. Don't mix-up the run of A's at the start with the run of A's near the end.
There's nothing too complicated with just doing it the old-fashioned way:
Func<string, object> groupingFunction = s => s.Substring(0, 1);
IEnumerable<string> input = new List<string>() {"golf", "hip", "..." };
var output = new List<string>();
if (!input.Any())
{
return output;
}
var lastItem = input.First();
var lastKey = groupingFunction(lastItem);
foreach (var currentItem in input.Skip(1))
{
var currentKey = groupingFunction(str);
if (!currentKey.Equals(lastKey))
{
output.Add(lastItem);
}
lastKey = currentKey;
lastItem = currentItem;
}
output.Add(lastItem);
You could also turn this into a generic extension method as Tim Schmelter has done; I have already taken a couple steps to generalize the code on purpose (using object as the key type and IEnumerable<T> as the input type).
You could use this extension that can group by adjacent/consecutive elements:
public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
TKey last = default(TKey);
bool haveLast = false;
List<TSource> list = new List<TSource>();
foreach (TSource s in source)
{
TKey k = keySelector(s);
if (haveLast)
{
if (!k.Equals(last))
{
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
list = new List<TSource>();
list.Add(s);
last = k;
}
else
{
list.Add(s);
last = k;
}
}
else
{
list.Add(s);
last = k;
haveLast = true;
}
}
if (haveLast)
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
}
public class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource>
{
public TKey Key { get; set; }
private List<TSource> GroupList { get; set; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.Generic.IEnumerable<TSource>)this).GetEnumerator();
}
System.Collections.Generic.IEnumerator<TSource> System.Collections.Generic.IEnumerable<TSource>.GetEnumerator()
{
foreach (var s in GroupList)
yield return s;
}
public GroupOfAdjacent(List<TSource> source, TKey key)
{
GroupList = source;
Key = key;
}
}
Then it's easy:
var words = new List<string>{"golf", "hip", "hop", "hotel", "grass", "world", "wee"};
IEnumerable<string> lastWordOfConsecutiveFirstCharGroups = words
.GroupAdjacent(str => str[0])
.Select(g => g.Last());
Output:
string.Join(",", lastWordOfConsecutiveFirstCharGroups); // golf,hotel,grass,wee
Your other sample:
words=new List<string>{"apples", "armies", "black", "beer", "bastion", "cat", "cart", "able", "art", "bark"};
lastWordOfConsecutiveFirstCharGroups = words
.GroupAdjacent(str => str[0])
.Select(g => g.Last());
Output:
string.Join(",", lastWordOfConsecutiveFirstCharGroups); // armies,bastion,cart,art,bark
Demonstration
Try this algoritm
var words = new List<string> { "golf", "hip", "hop", "hotel", "grass", "world", "wee" };
var newList = new List<string>();
int i = 0;
while (i < words.Count - 1 && i <= words.Count)
{
if (words[i][0] != words[i+1][0])
{
newList.Add(words[i]);
i++;
}
else
{
var j = i;
while ( j < words.Count - 1 && words[j][0] == words[j + 1][0])
{
j++;
}
newList.Add(words[j]);
i = j+1;
}
}
You can use following extension method to split your sequence into groups (i.e. sub-sequnces) by some condition:
public static IEnumerable<IEnumerable<T>> Split<T, TKey>(
this IEnumerable<T> source, Func<T, TKey> keySelector)
{
var group = new List<T>();
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
else
{
TKey currentKey = keySelector(iterator.Current);
var keyComparer = Comparer<TKey>.Default;
group.Add(iterator.Current);
while (iterator.MoveNext())
{
var key = keySelector(iterator.Current);
if (keyComparer.Compare(currentKey, key) != 0)
{
yield return group;
currentKey = key;
group = new List<T>();
}
group.Add(iterator.Current);
}
}
}
if (group.Any())
yield return group;
}
And getting your expected results looks like:
string[] words = { "golf", "hip", "hop", "hotel", "grass", "world", "wee" };
var result = words.Split(w => w[0])
.Select(g => g.Last());
Result:
golf
hotel
grass
wee
Because your input is a List<>, so I think this should work for you with an acceptable performance and especially it's very concise:
var result = words.Where((x, i) => i == words.Count - 1 ||
words[i][0] != words[i + 1][0]);
You can append ToList() on the result to get a List<string> if you want.
I went with
/// <summary>
/// Given a list, return the last value from each run of similar items.
/// </summary>
public static IEnumerable<T> WithoutDuplicates<T>(this IEnumerable<T> source, Func<T, T, bool> similar)
{
Contract.Requires(source != null);
Contract.Requires(similar != null);
Contract.Ensures(Contract.Result<IEnumerable<T>>().Count() <= source.Count(), "Result should be at most as long as original list");
T last = default(T);
bool first = true;
foreach (var item in source)
{
if (!first && !similar(item, last))
yield return last;
last = item;
first = false;
}
if (!first)
yield return last;
}

How to split list into all the cases sublists using LINQ?

I would like to to split list into all the cases SubLists using LINQ?
For example :
List contains : {"a", "b", "c"}
I would like to make list of lists where the result is : {"a", "ab", "abc"}
public List<List<Alphabet>> ListofLists (Stack<String> Pile)
{
var listoflists = new List<List<Alphabet>>();
var list = new List<Alphabet>();
foreach (var temp in from value in Pile where value != "#" select new Alphabet(value))
{
list.Add(temp);
listoflists.Add(list);
}
return listoflists;
}
This method will allow you to do this.
IEnumerable<IEnumerable<T>> SublistSplit<T>(this IEnumerable<T> source)
{
if (source == null) return null;
var list = source.ToArray();
for (int i = 0; i < list.Length; i++)
{
yield return new ArraySegment<T>(list, 0, i);
}
}
In case of strings:
IEnumerable<string> SublistSplit<T>(this IEnumerable<string> source)
{
if (source == null) return null;
var sb = new StringBuilder();
foreach (var x in source)
{
sb.Append(x);
yield return sb.ToString();
}
}
If you want to yield the intermediate values of an accumulation you could define your own extension method:
public IEnumerable<TAcc> Scan<T, TAcc>(this IEnumerable<T> seq, TAcc init, Func<T, TAcc, TAcc> acc)
{
TAcc current = init;
foreach(T item in seq)
{
current = acc(item, current);
yield return current;
}
}
then your example would be:
var strings = new[] {"a", "b", "c"}.Scan("", (str, acc) => str + acc);
for lists, you'll have to copy them each time:
List<Alphabet> input = //
List<List<Alphabet>> output = input.Scan(new List<Alphabet>(), (a, acc) => new List<Alphabet(acc) { a }).ToList();
Note that copying the intermediate List<T> instances could be inefficient, so you may want to consider using an immutable structure instead.

Intersperse a List<> [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Extension method for Enumerable.Intersperse?
I have a List<string> like this:
"foo", "bar", "cat"
I want it to look like this:
"foo", "-", "bar", "-", "cat"
Is there a C# method that does that?
You can make an extension that returns an IEnumerable<T> with interspersed items:
public static class ListExtensions {
public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> items, T separator) {
bool first = true;
foreach (T item in items) {
if (first) {
first = false;
} else {
yield return separator;
}
yield return item;
}
}
}
The advantage of this is that you don't have to clutter your list with the extra items, you can just use it as it is returned:
List<string> words = new List<string>() { "foo", "bar", "cat" };
foreach (string s in words.Intersperse("-")) {
Console.WriteLine(s);
}
You can of course get the result as a list if you need that:
words = words.Intersperse("-").ToList();
Here is an implementation out of my personal toolbox. It's more general than what you require.
For your particular example, you would write
var list = new List<string> { "foo", "bar", "cat" };
var result = list.InterleaveWith(Enumerable.Repeat("-", list.Count - 1));
See it in action.
public List<string> GetNewList(List<string> list, string s)
{
var result = new List<string>();
foreach (var l in list)
{
result.Add(l);
result.Add(s);
}
result.RemoveAt(result.Count - 1);
return result;
}
you can use this method to get you list
var result = GetNewList(str,"-");
Hah! I've just written something for just this purpose!
public static IEnumerable<T> Interleave<T>(params IEnumerable<T>[] arrays)
{
var enumerators = arrays.Select(array => array.GetEnumerator()).ToArray();
var finished = true;
do
{
finished = true;
foreach (var enumerator in enumerators)
{
if (enumerator.MoveNext())
{
yield return enumerator.Current;
finished = false;
}
}
} while (finished == false);
}
This will interleave the specified enumerables in the order you choose.
Then you just fill an enumerable with dashes and interleave that with the original array. It can do more complex things as well.
A while ago I wrote an extension method for this kind of thing:
public static IEnumerable<T>
Join<T>(this IEnumerable<T> src, Func<T> separatorFactory)
{
var srcArr = src.ToArray();
for (int i = 0; i < srcArr.Length; i++)
{
yield return srcArr[i];
if(i<srcArr.Length-1)
{
yield return separatorFactory();
}
}
}
You can use it as follows:
myList.Join(() => "-").ToList()
EDIT:
As pointed out, my previous code results to a string not an array of strings. So here's my edited code:
var list = new List<string> { "foo", "bar", "cat" };
var result = string.Join("-", list.Select(x => x.ToString()).ToArray());
string pattern = "(-)";
string[] test = Regex.Split(result, pattern);
foreach(var str in test)
Console.WriteLine(str);
Retained old code for comparison purposes:
var list = new List<string> { "foo", "bar", "cat" };
var result = string.Join("-", list.Select(x => x.ToString()).ToArray());
Console.WriteLine(result); // prints "foo-bar-cat
Refer to this post. Cheers!
Yes, its a custom method you would write:
List<string> AddDashesToList(List<string> list)
{
if(list.Count > 1)
{
var newList = new List<string>();
foreach(item in list)
{
newList.Add(item);
newList.Add("-");
}
newList.RemoveAt(newList.Count-1);
return newList;
}
else
{
return list;
}
}

C# Permutation of an array of arraylists?

I have an ArrayList[] myList and I am trying to create a list of all the permutations of the values in the arrays.
EXAMPLE: (all values are strings)
myList[0] = { "1", "5", "3", "9" };
myList[1] = { "2", "3" };
myList[2] = { "93" };
The count of myList can be varied so its length is not known beforehand.
I would like to be able to generate a list of all the permutations similar to the following (but with some additional formatting).
1 2 93
1 3 93
5 2 93
5 3 93
3 2 93
3 3 93
9 2 93
9 3 93
Does this make sense of what I am trying to accomplish? I can't seem to come up with a good method for doing this, (if any).
Edit:
I am not sure if recursion would interfere with my desire to format the output in my own manner. Sorry I did not mention before what my formatting was.
I want to end up building a string[] array of all the combinations that follows the format like below:
for the "1 2 93" permutation
I want the output to be "val0=1;val1=2;val2=93;"
I will experiment with recursion for now. Thank you DrJokepu
I'm surprised nobody posted the LINQ solution.
from val0 in new []{ "1", "5", "3", "9" }
from val1 in new []{ "2", "3" }
from val2 in new []{ "93" }
select String.Format("val0={0};val1={1};val2={2}", val0, val1, val2)
Recursive solution
static List<string> foo(int a, List<Array> x)
{
List<string> retval= new List<string>();
if (a == x.Count)
{
retval.Add("");
return retval;
}
foreach (Object y in x[a])
{
foreach (string x2 in foo(a + 1, x))
{
retval.Add(y.ToString() + " " + x2.ToString());
}
}
return retval;
}
static void Main(string[] args)
{
List<Array> myList = new List<Array>();
myList.Add(new string[0]);
myList.Add(new string[0]);
myList.Add(new string[0]);
myList[0] = new string[]{ "1", "5", "3", "9" };
myList[1] = new string[] { "2", "3" };
myList[2] = new string[] { "93" };
foreach (string x in foo(0, myList))
{
Console.WriteLine(x);
}
Console.ReadKey();
}
Note that it would be pretty easy to return a list or array instead of a string by changing the return to be a list of lists of strings and changing the retval.add call to work with a list instead of using concatenation.
How it works:
This is a classic recursive algorithm. The base case is foo(myList.Count, myList), which returns a List containing one element, the empty string. The permutation of a list of n string arrays s1, s2, ..., sN is equal to every member of sA1 prefixed to the permutation of n-1 string arrays, s2, ..., sN. The base case is just there to provide something for each element of sN to be concatenated to.
I recently ran across a similar problem in a project of mine and stumbled on this question. I needed a non-recursive solution that could work with lists of arbitrary objects. Here's what I came up with. Basically I'm forming a list of enumerators for each of the sub-lists and incrementing them iteratively.
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<IEnumerable<T>> lists)
{
// Check against an empty list.
if (!lists.Any())
{
yield break;
}
// Create a list of iterators into each of the sub-lists.
List<IEnumerator<T>> iterators = new List<IEnumerator<T>>();
foreach (var list in lists)
{
var it = list.GetEnumerator();
// Ensure empty sub-lists are excluded.
if (!it.MoveNext())
{
continue;
}
iterators.Add(it);
}
bool done = false;
while (!done)
{
// Return the current state of all the iterator, this permutation.
yield return from it in iterators select it.Current;
// Move to the next permutation.
bool recurse = false;
var mainIt = iterators.GetEnumerator();
mainIt.MoveNext(); // Move to the first, succeeds; the main list is not empty.
do
{
recurse = false;
var subIt = mainIt.Current;
if (!subIt.MoveNext())
{
subIt.Reset(); // Note the sub-list must be a reset-able IEnumerable!
subIt.MoveNext(); // Move to the first, succeeds; each sub-list is not empty.
if (!mainIt.MoveNext())
{
done = true;
}
else
{
recurse = true;
}
}
}
while (recurse);
}
}
You could use factoradics to generate the enumeration of permutations. Try this article on MSDN for an implementation in C#.
This will work no matter how many arrays you add to your myList:
static void Main(string[] args)
{
string[][] myList = new string[3][];
myList[0] = new string[] { "1", "5", "3", "9" };
myList[1] = new string[] { "2", "3" };
myList[2] = new string[] { "93" };
List<string> permutations = new List<string>(myList[0]);
for (int i = 1; i < myList.Length; ++i)
{
permutations = RecursiveAppend(permutations, myList[i]);
}
//at this point the permutations variable contains all permutations
}
static List<string> RecursiveAppend(List<string> priorPermutations, string[] additions)
{
List<string> newPermutationsResult = new List<string>();
foreach (string priorPermutation in priorPermutations)
{
foreach (string addition in additions)
{
newPermutationsResult.Add(priorPermutation + ":" + addition);
}
}
return newPermutationsResult;
}
Note that it's not really recursive. Probably a misleading function name.
Here is a version that adheres to your new requirements. Note the section where I output to console, this is where you can do your own formatting:
static void Main(string[] args)
{
string[][] myList = new string[3][];
myList[0] = new string[] { "1", "5", "3", "9" };
myList[1] = new string[] { "2", "3" };
myList[2] = new string[] { "93" };
List<List<string>> permutations = new List<List<string>>();
foreach (string init in myList[0])
{
List<string> temp = new List<string>();
temp.Add(init);
permutations.Add(temp);
}
for (int i = 1; i < myList.Length; ++i)
{
permutations = RecursiveAppend(permutations, myList[i]);
}
//at this point the permutations variable contains all permutations
foreach (List<string> list in permutations)
{
foreach (string item in list)
{
Console.Write(item + ":");
}
Console.WriteLine();
}
}
static List<List<string>> RecursiveAppend(List<List<string>> priorPermutations, string[] additions)
{
List<List<string>> newPermutationsResult = new List<List<string>>();
foreach (List<string> priorPermutation in priorPermutations)
{
foreach (string addition in additions)
{
List<string> priorWithAddition = new List<string>(priorPermutation);
priorWithAddition.Add(addition);
newPermutationsResult.Add(priorWithAddition);
}
}
return newPermutationsResult;
}
What you are asking for is called the Cartesian Product. Once you know what its called, there are several similar questions on Stack Overflow. They all seem to end up pointing to an answer which ended up written as a blog post:
http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx
Non-recursive solution:
foreach (String s1 in array1) {
foreach (String s2 in array2) {
foreach (String s3 in array3) {
String result = s1 + " " + s2 + " " + s3;
//do something with the result
}
}
}
Recursive solution:
private ArrayList<String> permute(ArrayList<ArrayList<String>> ar, int startIndex) {
if (ar.Count == 1) {
foreach(String s in ar.Value(0)) {
ar.Value(0) = "val" + startIndex + "=" + ar.Value(0);
return ar.Value(0);
}
ArrayList<String> ret = new ArrayList<String>();
ArrayList<String> tmp1 ar.Value(0);
ar.remove(0);
ArrayList<String> tmp2 = permute(ar, startIndex+1);
foreach (String s in tmp1) {
foreach (String s2 in tmp2) {
ret.Add("val" + startIndex + "=" + s + " " + s2);
}
}
return ret;
}
Here is a generic recursive function that I wrote (and an overload that may be convenient to call):
Public Shared Function GetCombinationsFromIEnumerables(ByRef chain() As Object, ByRef IEnumerables As IEnumerable(Of IEnumerable(Of Object))) As List(Of Object())
Dim Combinations As New List(Of Object())
If IEnumerables.Any Then
For Each v In IEnumerables.First
Combinations.AddRange(GetCombinationsFromIEnumerables(chain.Concat(New Object() {v}).ToArray, IEnumerables.Skip(1)).ToArray)
Next
Else
Combinations.Add(chain)
End If
Return Combinations
End Function
Public Shared Function GetCombinationsFromIEnumerables(ByVal ParamArray IEnumerables() As IEnumerable(Of Object)) As List(Of Object())
Return GetCombinationsFromIEnumerables(chain:=New Object() {}, IEnumerables:=IEnumerables.AsEnumerable)
End Function
And the equivalent in C#:
public static List<object[]> GetCombinationsFromIEnumerables(ref object[] chain, ref IEnumerable<IEnumerable<object>> IEnumerables)
{
List<object[]> Combinations = new List<object[]>();
if (IEnumerables.Any) {
foreach ( v in IEnumerables.First) {
Combinations.AddRange(GetCombinationsFromIEnumerables(chain.Concat(new object[] { v }).ToArray, IEnumerables.Skip(1)).ToArray);
}
} else {
Combinations.Add(chain);
}
return Combinations;
}
public static List<object[]> GetCombinationsFromIEnumerables(params IEnumerable<object>[] IEnumerables)
{
return GetCombinationsFromIEnumerables(chain = new object[], IEnumerables = IEnumerables.AsEnumerable);
}
Easy to use:
Dim list1 = New String() {"hello", "bonjour", "hallo", "hola"}
Dim list2 = New String() {"Erwin", "Larry", "Bill"}
Dim list3 = New String() {"!", ".."}
Dim result = MyLib.GetCombinationsFromIEnumerables(list1, list2, list3)
For Each r In result
Debug.Print(String.Join(" "c, r))
Next
or in C#:
object list1 = new string[] {"hello","bonjour","hallo","hola"};
object list2 = new string[] {"Erwin", "Larry", "Bill"};
object list3 = new string[] {"!",".."};
object result = MyLib.GetCombinationsFromIEnumerables(list1, list2, list3);
foreach (r in result) {
Debug.Print(string.Join(' ', r));
}
Here is a version which uses very little code, and is entirely declarative
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> collection) where T : IComparable
{
if (!collection.Any())
{
return new List<IEnumerable<T>>() {Enumerable.Empty<T>() };
}
var sequence = collection.OrderBy(s => s).ToArray();
return sequence.SelectMany(s => GetPermutations(sequence.Where(s2 => !s2.Equals(s))).Select(sq => (new T[] {s}).Concat(sq)));
}
class Program
{
static void Main(string[] args)
{
var listofInts = new List<List<int>>(3);
listofInts.Add(new List<int>{1, 2, 3});
listofInts.Add(new List<int> { 4,5,6 });
listofInts.Add(new List<int> { 7,8,9,10 });
var temp = CrossJoinLists(listofInts);
foreach (var l in temp)
{
foreach (var i in l)
Console.Write(i + ",");
Console.WriteLine();
}
}
private static IEnumerable<List<T>> CrossJoinLists<T>(IEnumerable<List<T>> listofObjects)
{
var result = from obj in listofObjects.First()
select new List<T> {obj};
for (var i = 1; i < listofObjects.Count(); i++)
{
var iLocal = i;
result = from obj in result
from obj2 in listofObjects.ElementAt(iLocal)
select new List<T>(obj){ obj2 };
}
return result;
}
}
Here's a non-recursive, non-Linq solution. I can't help feeling like I could have less looping and calculate the positions with division and modulo, but can't quite wrap my head around that.
static void Main(string[] args)
{
//build test list
List<string[]> myList = new List<string[]>();
myList.Add(new string[0]);
myList.Add(new string[0]);
myList.Add(new string[0]);
myList[0] = new string[] { "1", "2", "3"};
myList[1] = new string[] { "4", "5" };
myList[2] = new string[] { "7", "8", "9" };
object[][] xProds = GetProducts(myList.ToArray());
foreach(object[] os in xProds)
{
foreach(object o in os)
{
Console.Write(o.ToString() + " ");
}
Console.WriteLine();
}
Console.ReadKey();
}
static object[][] GetProducts(object[][] jaggedArray){
int numLists = jaggedArray.Length;
int nProducts = 1;
foreach (object[] oArray in jaggedArray)
{
nProducts *= oArray.Length;
}
object[][] productAry = new object[nProducts][];//holds the results
int[] listIdxArray = new int[numLists];
listIdxArray.Initialize();
int listPtr = 0;//point to current list
for(int rowcounter = 0; rowcounter < nProducts; rowcounter++)
{
//create a result row
object[] prodRow = new object[numLists];
//get values for each column
for(int i=0;i<numLists;i++)
{
prodRow[i] = jaggedArray[i][listIdxArray[i]];
}
productAry[rowcounter] = prodRow;
//move the list pointer
//possible states
// 1) in a list, has room to move down
// 2) at bottom of list, can move to next list
// 3) at bottom of list, no more lists left
//in a list, can move down
if (listIdxArray[listPtr] < (jaggedArray[listPtr].Length - 1))
{
listIdxArray[listPtr]++;
}
else
{
//can move to next column?
//move the pointer over until we find a list, or run out of room
while (listPtr < numLists && listIdxArray[listPtr] >= (jaggedArray[listPtr].Length - 1))
{
listPtr++;
}
if (listPtr < listIdxArray.Length && listIdxArray[listPtr] < (jaggedArray[listPtr].Length - 1))
{
//zero out the previous stuff
for (int k = 0; k < listPtr; k++)
{
listIdxArray[k] = 0;
}
listIdxArray[listPtr]++;
listPtr = 0;
}
}
}
return productAry;
}
One of the problems I encountred when I was doing this for a very large amount of codes was that with the example brian was given I actually run out of memory. To solve this I used following code.
static void foo(string s, List<Array> x, int a)
{
if (a == x.Count)
{
// output here
Console.WriteLine(s);
}
else
{
foreach (object y in x[a])
{
foo(s + y.ToString(), x, a + 1);
}
}
}
static void Main(string[] args)
{
List<Array> a = new List<Array>();
a.Add(new string[0]);
a.Add(new string[0]);
a.Add(new string[0]);
a[0] = new string[] { "T", "Z" };
a[1] = new string[] { "N", "Z" };
a[2] = new string[] { "3", "2", "Z" };
foo("", a, 0);
Console.Read();
}
private static void GetP(List<List<string>> conditions, List<List<string>> combinations, List<string> conditionCombo, List<string> previousT, int selectCnt)
{
for (int i = 0; i < conditions.Count(); i++)
{
List<string> oneField = conditions[i];
for (int k = 0; k < oneField.Count(); k++)
{
List<string> t = new List<string>(conditionCombo);
t.AddRange(previousT);
t.Add(oneField[k]);
if (selectCnt == t.Count )
{
combinations.Add(t);
continue;
}
GetP(conditions.GetRange(i + 1, conditions.Count - 1 - i), combinations, conditionCombo, t, selectCnt);
}
}
}
List<List<string>> a = new List<List<string>>();
a.Add(new List<string> { "1", "5", "3", "9" });
a.Add(new List<string> { "2", "3" });
a.Add(new List<string> { "93" });
List<List<string>> result = new List<List<string>>();
GetP(a, result, new List<string>(), new List<string>(), a.Count);
Another recursive function.

Categories