Lexicographically sorted list of lists of strings - c#

Currently, I am trying to implement a code to generate frequent sequences. In doing so I need to get an in-place sort of a list of lists of strings as follows:
List<List<string>> myList = new List<List<string>>();
List<string> input1 = new List<string>() {"a", "b", "d"};
List<string> input2 = new List<string>() {"a", "b", "c"};
myList.Add(input1);
myList.Add(input2);
The output that I need is:
myList = {{"a","b","c"},{"a","b","d"}};
I have tried to use myList.Sort() but it raised a System.InvalidOperationException.
I am not that good with LINQ so I haven't used anything of the sort.

How about :
myList = myList.OrderBy(s => string.Join(string.Empty, s)).ToList();
The trick is to sort according to the string made by the concatenation of each element of the child list.

If you want to solve with Sort() you may use this approach
myList.Sort((x, y) => x.Zip(y,(l1,l2) => string.Compare(l1,l2)).FirstOrDefault(c => c != 0));
Otherwise I would concartenate all items into a single string and compare those.
This is less efficient because the string objects have to be created first.
myList = myList.OrderBy(string.Concat).ToList();
Sample: https://dotnetfiddle.net/1VmohI

You can try below code:
List<string> input1 = new List<string>() { "a", "b", "d" };
List<string> input2 = new List<string>() { "a", "b", "c" };
//Instead of adding input as List<string>, add it as string
string delimiter = ",";
var input1Str = input1.Aggregate((i, j) => i + delimiter + j);
var input2Str = input2.Aggregate((i, j) => i + delimiter + j);
var myListStr = new List<string>();
myListStr.Add(input1Str);
myListStr.Add(input2Str);
myListStr.Sort();
//Now you can convert it into List<List<string>>
List<List<string>> myList = myListStr.Select(x => x.Split(',').ToList()).ToList();

You can also use
myList = myList.OrderBy(arr => arr[0])
.ThenBy(arr => arr[1])
.ThenBy(arr => arr[2])
.ToList();

Related

How can I create a list of each item in another list, and the count of how many times that item appears?

So I have a list of items -> A, B, C, D.
C and D are included more than once, and A and B, more than twice. This list can go on and on, so we do not know how many times an item will be included.
I need to create a new list that will have the item in one column and the number of instances of that item in another column, but I do not know how to do this. I may need to use a tuple or a class, but I am not fully sure how to implement either...
What you actually need is to Group the items of your list and perform a group operation, which is Count in your case to calculate how many times does it exist.
This is how you may initialize your list:
List<string> myList = new List<string>() { "A", "B", "C", "D", "A", "B", "C", "D", "A", "B" };
and then you will group it using GroupBy function and apply the Count aggregate function on each group.
myList
.GroupBy(item => item)
.Select(g => new {Key = g.Key, Count = g.Count()})
.ToList();
This will result in the table you need.
You can try like this:
var myList = new List<String>() { "A","B", "C", "D","A","B", "C", "D", "A","B"};
var grp = myList.GroupBy( x => x );
foreach( var g in grp )
{
Console.WriteLine( "{0} {1}", g.Key, g.Count() );
}
DOTNET FIDDLE
char[] items = new[] { 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B' };
Dictionary<char, int> counts = new();
foreach(char c in items)
{
if (counts.TryGetValue(c, out int n))
{
counts[c] = n + 1;
}
else
{
counts.Add(c, 1);
}
}
While not a one liner, a simple and fast option.
I may need to use a tuple or a class, but I am not fully sure how to implement either...
Since you mentioned you may want to use a class, here is an example:
public class TextCount
{
public string Text { get; set; }
public int Count { get; set; }
}
public class Program
{
public static void Main()
{
// Initialize the list of strings
List<string> data = new List<string> { "A", "B", "C", "D", "A", "B", "C", "D", "A", "B" };
// Use LINQ to group the strings by their value and count the number of occurrences of each string
List<TextCount> result = data
.GroupBy(s => s)
.Select(g => new TextCount { Text = g.Key, Count = g.Count() })
.ToList();
// Print the results
foreach (TextCount sc in result)
{
Console.WriteLine("{0}: {1}", sc.Text, sc.Count);
}
}
}
Demo: https://dotnetfiddle.net/2FRBbK

Find common items in list of lists of strings

Hi I have allLists that contains lists of string I want to find common items among these string lists
i have tried
var intersection = allLists
.Skip(1)
.Aggregate(
new HashSet<string>(allLists.First()),
(h, e) => { h.IntersectWith(e); return h);`
and also intersection ( hard code lists by index) all of them did not work when I tried
var inter = allLists[0].Intersect(allLists[1]).Intersect(allLists[2])
.Intersect(allLists[3]).ToList();
foreach ( string s in inter) Debug.WriteLine(s+"\n ");
So how am I going to do this dynamically and get common string items in the lists;
is there a way to avoid Linq?
Isn't this the easiest way?
var stringLists = new List<string>[]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "d", "b", "c" },
new List<string>(){ "a", "e", "c" }
};
var commonElements =
stringLists
.Aggregate((xs, ys) => xs.Intersect(ys).ToList());
I get a list with just "c" in it.
This also handles the case if elements within each list can be repeated.
I'd do it like this:
class Program
{
static void Main(string[] args)
{
List<string>[] stringLists = new List<string>[]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "d", "b", "c" },
new List<string>(){ "a", "e", "c" }
};
// Will contian only 'c' because it's the only common item in all three groups.
var commonItems =
stringLists
.SelectMany(list => list)
.GroupBy(item => item)
.Select(group => new { Count = group.Count(), Item = group.Key })
.Where(item => item.Count == stringLists.Length);
foreach (var item in commonItems)
{
Console.WriteLine(String.Format("Item: {0}, Count: {1}", item.Item, item.Count));
}
Console.ReadKey();
}
}
An item is a common item if it occurs in all groups hence the condition that its count must be equal to the number of groups:
.Where(item => item.Count == stringLists.Length)
EDIT:
I should have used the HashSet like in the question. For lists you can replace the SelectMany line with this one:
.SelectMany(list => list.Distinct())

Longest list in SortedList of Lists

I have a SortedList of Lists and I am interested in finding the KEY that corresponds to the longest list (list with the most items in it). In code, that looks like:
// how the list is defined:
var myList = new SortedList<long, List<string>>();
// EXAMPLE data only:
myList.Add(0, new List<string>());
myList[0].AddRange(new []{"a", "b", "c"});
myList.Add(8, new List<string>());
myList[8].AddRange(new []{"1", "2"});
myList.Add(23, new List<string>());
myList[23].AddRange(new []{"c", "d", "e", "f", "g"});
In the above example the result should be "23" since that is the key that goes with the longest list.
I know how to write this with a for loop, but I think this should be a simple to do with LINQ. That said, I can't seem to get the syntax quite right! Any help is appreciated!
There's maybe a more efficient way, but you can order by count (of value) descending, and take first.
myList.OrderByDescending(m => m.Value.Count()).First().Key;
of course, if you want all the keys with highest count (they may be multiple values with same length), you should do a group by count.
Something like that.
myList.GroupBy(m => m.Value.Count())
.OrderByDescending(m => m.Key)//I'm the key of the group by
.First()
.Select(g => g.Key);//I'm the key of the SortedList
So if you add to your sample an item with same list length
myList.Add(24, new List<string>());
myList[24].AddRange(new[] {"a", "b", "c", "d", "e"});
you will get 23 And 24.
same could be achieved with
from item in myList
let maxCount = myList.Max(x => x.Value.Count())
where item.Value.Count() == maxCount
select item.Key;
Whilst a sort will give you correct results, it requires O(n log n) time to execute, which is asymptotically higher than a simple O(n) sweep:
int maxLength = myList.Max(x => x.Value.Count);
var longestKeys = myList.Where(x => x.Value.Count == maxLength).Select(x => x.Key);
using MaxBy of morelinq
var key = myList.MaxBy(x => x.Value.Count()).Key;
Just for the sake of adding yet another way of doing it, you can achieve this with Linq's Aggregate method like so:
//Extension method
public static long MaxIndex(this SortedList<long, List<string>> list)
{
return list.Aggregate(
new { MaxValue = -1, Key = -1L },
((agg, current) => (current.Value.Count.CompareTo(agg.MaxValue) > 0 || agg.Key == -1) ?
new { MaxValue = current.Value.Count, Key = current.Key } :
new { MaxValue = agg.MaxValue, Key = agg.Key })).
Key;
}
// how the list is defined:
var myList = new SortedList<long, List<string>>();
// EXAMPLE data only:
myList.Add(0, new List<string>());
myList[0].AddRange(new[] { "a", "b", "c" });
myList.Add(8, new List<string>());
myList[8].AddRange(new[] { "1", "2" });
myList.Add(23, new List<string>());
myList[23].AddRange(new[] { "c", "d", "e", "f", "g" });
var idx = myList.MaxIndex();
This is adapted from this SO answer: https://stackoverflow.com/a/15068695/172769
Cheers

Uncommon elements in multiple lists

I have multiple lists and i want to find distinct uncommon elements in everylist.
I came to a point but couldnt managed to get the uncommon elements but ended up common elements.
Here is the code:
private void button1_Click(object sender, EventArgs e)
{
List<DataTable> dataTables = new List<DataTable>();
List<string> c = new List<string>();
c.Add("A");
c.Add("B");
c.Add("C");
dataTables.Add(ss(c));
c.Add("B");
c.Add("C");
dataTables.Add(ss(c));
c.Add("A");
c.Add("B");
dataTables.Add(ss(c));
var setsOfIds = dataTables.Select(t => t.AsEnumerable().Select(x => x.Field<string>("ELIGIBLE")).OfType<string>());
var commonIds = IntersectAll<string>(setsOfIds);
var rows = dataTables.SelectMany(t => t.AsEnumerable()).Where(r => commonIds.Contains(r.Field<string>("ELIGIBLE")));
var resultRows = rows.GroupBy(r => r.Field<string>("TEDARIKCI")).Select(r => r.First());
}
private DataTable ss(List<string> aa)
{
DataTable dt = new DataTable("x");
dt.Columns.Add("ELIGIBLE", typeof(string));
DataRow dr = null;//= dt.NewRow();
foreach (string item in aa)
{
dr = dt.NewRow();
dr[0] = item;
dt.Rows.Add(dr);
}
return dt;
}
public List<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists)
{
HashSet<T> hashSet = null;
foreach (var list in lists)
{
if (hashSet == null)
{
hashSet = new HashSet<T>(list);
}
else
{
hashSet.IntersectWith(list);
}
}
return hashSet == null ? new List<T>() : hashSet.ToList();
}
IntersectAll does not help me because it gives me A,B,C. But i only want B (which is common to all of each list)
I would appreciate if s.o. could give a sample. This is driving me crazy!! Pleeeeaaseeee
var l1 = new List<string> {"A", "B", "C", "D"};
var l2 = new List<string> {"B", "C", "E"};
var l3 = new List<string> {"A", "B"};
var c = l1.Intersect(l2).Intersect(l3);
returns "B"
var uc1= l1.Except(l2).Except(l3);
var uc2= l2.Except(l1).Except(l3);
var uc3= l3.Except(l1).Except(l2);
var uc=uc1.Union(uc2).Union(uc3);
should give:
uc1 = "D"
uc2 = "E"
uc3 = {empty}
uc = "D","E"
In your code, the first list had 3 elements (ABC), the second had 5(ABCBC) and the third had 7 (ABCBCAB).
In other words, all the list had A, B, C ..
Creating new List before adding to dataTable would solve this issue
List<DataTable> dataTables = new List<DataTable>();
List<string> c = new List<string>();
c.Add("A");
c.Add("B");
c.Add("C");
dataTables.Add(ss(c));
List<string> c2 = new List<string>();//New List
c2.Add("B");
c2.Add("C");
dataTables.Add(ss(c2));
List<string> c3 = new List<string>();//New List
c3.Add("A");
c3.Add("B");
dataTables.Add(ss(c3));
var setsOfIds = dataTables.Select(t => t.AsEnumerable().Select(x => x.Field<string>("ELIGIBLE")).OfType<string>());
var commonIds = IntersectAll<string>(setsOfIds);
Here commonIds list would contain only one element B
It's not entirely clear what you want. You seem to be asking for a method to find elements that only appear in one list (no 2 lists have them in common), but your expected results seem to suggest you want to find elements that ALL lists have in common. In any case, both are fairly easy with Linq.
This will find all elements that appear exactly once in all lists:
public IEnumerable<T> FindUniques<T>(IEnumerable<IEnumerable<T>> lists)
{
return lists.SelectMany(x => x).GroupBy(x => x)
.Where(g => !g.Skip(1).Any())
.Select(g => g.First());
}
This fill find all elements that appear in every list:
public IEnumerable<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists)
{
return lists.Aggregate((a, x) => a.Intersect(x));
}
For example:
var list1 = new[] { "A", "B", "C" };
var list2 = new[] { "B", "C", "D" };
var list3 = new[] { "C", "D", "E" };
FindUniques(new[] { list1, list2, list3 }); // { "A", "E" }
IntersectAll(new[] { list1, list2, list3 }); // { "C" }

Concatenate a constant string to each item in a List<string> using LINQ

I am using C# and .Net 4.0.
I have a List<string> with some values, say x1, x2, x3. To each of the value in the List<string>, I need to concatenate a constant value, say "y" and get back the List<string> as x1y, x2y and x3y.
Is there a Linq way to do this?
List<string> yourList = new List<string>() { "X1", "Y1", "X2", "Y2" };
yourList = yourList.Select(r => string.Concat(r, 'y')).ToList();
list = list.Select(s => s + "y").ToList();
An alternative, using ConvertAll:
List<string> l = new List<string>(new [] {"x1", "x2", "x3"} );
List<string> l2 = l.ConvertAll(x => x + "y");
You can use Select for that
var list = new List<string>(){ "x1", "x2" };
list = list.Select(s => s + "y").ToList();

Categories