How to convert IEnumerable<IEnumerable<IGrouping<int,string>>> to IEnumerable<IEnumerable<string>> - c#

I'm trying to partition some comma separated lines into groups of size 2 at max.
How can i convert the collection of groups to list of lists as below?
I expect the partitions to be 3 first and then 4 after grouping.
List<string> chunk = new List<string>()
{
"a,b,c",
"a,d,e",
"b,c,d",
"b,e,d",
"b,f,g",
"e"
};
var partitons = chunk.GroupBy(c => c.Split(',')[0], (key, g) => g);
var groups = partitons.Select(x => x.Select((i, index) => new { i, index }).GroupBy(g => g.index / 2, e => e.i));
IEnumerable<IEnumerable<string>> parts = groups.Select(???)

This is what I wanted
var parts = groups.SelectMany(x => x).Select(y => y.Select(z => z));

Try this:
partitons = groups.Select(x => x.SelectMany(y => y));
I get this:

Related

Longest repeating sequence using linq only

As the title says i have a task to find the longest repeating sequence in a string and it has to be done with linq only - no ifs, no loop, no try, assignment is only allowed on initialization of variables, recursion is allowed. I've found the solution online and i understand what is happening but i can't transform it to linq -I'm not that familiar with it. I would greatly appreciate if someone could help me. Here is a link to what ive found -https://www.javatpoint.com/program-to-find-longest-repeating-sequence-in-a-string.
List<int> a = new List<int> {1, 2, 1, 2, 1, 2, 3, 2, 1, 2};
List<List<int>> aa = new List<List<int>>();
outerLoop(a);
var max = aa.Max(x => x.Count);
var m = from v in aa
where v.Count == max
select v;
m.Dump();
void outerLoop(List<int> list)
{
List<int> f = new List<int>();
f.AddRange(list.Skip(list.Count-1).Take(list.Count).ToList());
innerLoop(list, list.Skip(1).Take(list.Count).ToList());
f.ForEach(k => outerLoop(list.Skip(1).Take(list.Count).ToList()));
}
void innerLoop(List<int> l, List<int> subList)
{
List<int> f = new List<int>();
f.AddRange(subList.Skip(subList.Count-1).Take(subList.Count).ToList());
var tt = l.TakeWhile((ch, i) => i < subList.Count && subList[i] == ch).ToList();
aa.Add(tt);
f.ForEach(k => innerLoop(l, subList.Skip(1).Take(subList.Count).ToList()));
}
so i came up with this "beauty", i don't think it's good code but i think it works. If anyone is interested and wants to make suggestions how to make it better, they are more than welcome to :)
if input is int[] x= {1, 2, 1, 2, 1, 2, 3, 2, 1, 2}
result should be 1212
Give this a go:
List<int> words = new List<int> { 1, 2, 1, 2, 1, 2, 3, 2, 1, 2 };
string result =
words
.Select((c, i) => i)
.SelectMany(i => Enumerable.Range(1, words.Count - i).Select(j => words.Skip(i).Take(j)), (i, w) => new { i, w })
.GroupBy(x => String.Join(",", x.w), x => x.i)
.Where(x => x.Skip(1).Any())
.Select(x => x.Key)
.OrderByDescending(x => x.Length)
.First();
That gives me 1,2,1,2.
If you want one that actually works with strings, try this:
var word = "supercalifragilisticexpialidocious";
string result =
word
.Select((c, i) => i)
.SelectMany(i => Enumerable.Range(1, word.Length - i).Select(j => word.Skip(i).Take(j)), (i, w) => new { i, w })
.GroupBy(x => new string(x.w.ToArray()), x => x.i)
.Where(x => x.Skip(1).Any())
.Select(x => x.Key)
.OrderByDescending(x => x.Length)
.First();
That gives me ali.
Here's a slightly more understandable version:
var word = "supercalifragilisticexpialidocious";
string result =
(
from i in Enumerable.Range(0, word.Length)
from j in Enumerable.Range(1, word.Length - i)
group i by word.Substring(i, j) into gis
where gis.Skip(1).Any()
orderby gis.Key.Length descending
select gis.Key
).First();
Here is my version. It isn't a single LINQ expression, but does use only LINQ. It does return all same length subsequences if there are multiple answers. It should work any type of sequence. It was written to only use standard LINQ methods.
It uses GroupBy with a string key to implement a sequence Distinct. (Because of this trick, lists that contain items with commas might not work right.) In production code, I would use a Distinct with an IEqualityComparer for sequences based on SequenceEqual. It also has a separate step for finding the maximum repeated sequence length and then finding all the matching sequences, in production code I would use a MaxBy extension.
Update: Since I was using GroupBy for DistinctBy, I realized I could just use that to count the subsequence repeats directly rather than search for them.
var repeaters = Enumerable.Range(0, words.Count) // starting positions
.SelectMany(n => Enumerable.Range(1, (words.Count - n) / 2).Select(l => words.Skip(n).Take(l).ToList())) // subseqs from each starting position
.GroupBy(s => String.Join(",", s), (k, sg) => new { seq = sg.First(), Repeats = sg.Count() }) // count each sequence
.Where(sr => sr.Repeats > 1) // only keep repeated sequences
.Select(sr => sr.seq); // no longer need counts
var maxRepeaterLen = repeaters.Select(ss => ss.Count()).Max(); // find longest repeated sequence's length
var maxLenRepeaters = repeaters.Where(ss => ss.Count() == maxRepeaterLen); // return all sequences matching longest length

Evenly distribute males and females in list with LINQ

I have Class1 like:
{
string Name,
string Sex
}
And I have a List<Class1> with 100 items where 50 are Males and 50 are Females, how do I get 10 groups of 5Males and 5Females each with LINQ?
I already manage to get the list grouped in 10 groups but not distributed evenly by sex.
var foo = My100List.Select((person, index) => new {person, index})
.GroupBy(x => x.index%10)
.Select(i => new Group
{
Name= "Group" + i.Key,
Persons= i.Select(y => y.person).ToList()
});
The code above don't distribute by sex.
Try this (untested):
int groupSize = 5;
var foo = My100List.GroupBy(x => x.Sex)
.SelectMany(g => g.Select((x, i) => new { Person = x, Group = i / groupSize}))
.GroupBy(x => x.Group)
.Select(g => new Group
{
Name = "Group" + g.Key,
Persons = g.Select(x => x.Person).ToList()
});
EDIT
Tested and confirmed. The above code works.
Add .OrderBy for sex before the .Select
Tested and working:
var foo = My100List.OrderBy(p => p.Sex).Select((person, index) => new {person, index})
.GroupBy(x => x.index%10)
.Select(i => new Group
{
Name= "Group" + i.Key,
Persons= i.Select(y => y.person).ToList()
});

implement dense rank with linq

Using the following linq code, how can I add dense_rank to my results? If that's too slow or complicated, how about just the rank window function?
var x = tableQueryable
.Where(where condition)
.GroupBy(cust=> new { fieldOne = cust.fieldOne ?? string.Empty, fieldTwo = cust.fieldTwo ?? string.Empty})
.Where(g=>g.Count()>1)
.ToList()
.SelectMany(g => g.Select(cust => new {
cust.fieldOne
, cust.fieldTwo
, cust.fieldThree
}));
This does a dense_rank(). Change the GroupBy and the Order according to your need :)
Basically, dense_rank is numbering the ordered groups of a query so:
var DenseRanked = data.Where(item => item.Field2 == 1)
//Grouping the data by the wanted key
.GroupBy(item => new { item.Field1, item.Field3, item.Field4 })
.Where(#group => #group.Any())
// Now that I have the groups I decide how to arrange the order of the groups
.OrderBy(#group => #group.Key.Field1 ?? string.Empty)
.ThenBy(#group => #group.Key.Field3 ?? string.Empty)
.ThenBy(#group => #group.Key.Field4 ?? string.Empty)
// Because linq to entities does not support the following select overloads I'll cast it to an IEnumerable - notice that any data that i don't want was already filtered out before
.AsEnumerable()
// Using this overload of the select I have an index input parameter. Because my scope of work is the groups then it is the ranking of the group. The index starts from 0 so I do the ++ first.
.Select((#group , i) => new
{
Items = #group,
Rank = ++i
})
// I'm seeking the individual items and not the groups so I use select many to retrieve them. This overload gives me both the item and the groups - so I can get the Rank field created above
.SelectMany(v => v.Items, (s, i) => new
{
Item = i,
DenseRank = s.Rank
}).ToList();
Another way is as specified by Manoj's answer in this question - But I prefer it less because of the selecting twice from the table.
So if I understand this correctly, the dense rank is the index of the group it would be when the groups are ordered.
var query = db.SomeTable
.GroupBy(x => new { x.Your, x.Key })
.OrderBy(g => g.Key.Your).ThenBy(g => g.Key.Key)
.AsEnumerable()
.Select((g, i) => new { g, i })
.SelectMany(x =>
x.g.Select(y => new
{
y.Your,
y.Columns,
y.And,
y.Key,
DenseRank = x.i,
}
);
var denseRanks = myDb.tblTestReaderCourseGrades
.GroupBy(x => new { x.Grade })
.OrderByDescending(g => g.Key.Grade)
.AsEnumerable()
.Select((g, i) => new { g, i })
.SelectMany(x =>
x.g.Select(y => new
{
y.Serial,
Rank = x.i + 1,
}
));

Selecting array values from specific indexes order by index array

I want to select array values from specific indexes
Now I have this.
var xs = new[] { 11,12,13,14,15 };
var ind = new[] { 3,2,1,0 };
var results = xs.Where((x, idx) => ind.Contains(idx)).ToArray();
The result is {11,12,13,14}
However, I want my result to be ordered by index array which should be {14,13,12,11}
Thank you very much
var results = ind.Select(i => xs[i]).ToArray();
var array = xs.Zip(ind, (x, i) => new Tuple<int, int>(x, i))
.OrderBy(t => t.Item2)
.Select(t => t.Item1)
.ToArray();

How to compare 2 list by characters content and its correspondents double values?

I have 2 lists: a string list and a double list with same length and with same index of correspondence. I need to compare all the strings, find the indexes of the list that has the same characters, independent of its order, and delete the highest double value that corresponds to both,
Example:
List<string> str= new List<string>();
str.add("efc");
str.add("abc");
str.add("cde");
str.add("cab");
str.add("fbc");
List<double> vlr= new List<double>();
vlr.add(0.1);
vlr.add(0.5);
vlr.add(0.4);
vlr.add(0.2);
vlr.add(0.3);
and this case, "abc" => (0.5) must be deleted because "cab" has the same characters AND lower correspondent value =>(0.2).
There is a lambda expression for this 2 arrays??
What I've tried:
var distinct = list .Select((str, idx) => new { Str = str, Idx = idx })
.GroupBy(pair => new HashSet<char>(pair.Str), HashSet<char>.CreateSetComparer())
.Select(grp => grp.OrderBy(p => p.Idx).First())
.ToList();
Here's one way to solve it:
// Pair the strings with their correspondence values
var pairs = str.Zip(vlr, (s, d) => new {s, d});
// Group using a sorted string, eliminating differences due to character order
var groups = pairs.GroupBy(x => new string(x.s.ToCharArray().OrderBy(c => c).ToArray()));
// For each group, retain the item with the lowest correspondence value
var filtered = groups.Select(x => x.OrderBy(y => y.d).First().s);
var newDict = str.Zip(vlr, (s, d) => new { s, d })
.GroupBy(x => String.Join("", x.s.OrderBy(y => y)))
.Select(g => g.OrderBy(x => x.d).First())
.ToDictionary(x => x.s, x => x.d);
here is the code:
var group = str.GroupBy(s => string.Join("", s.ToCharArray().OrderBy(c => c)));
var _vlr = group.Select(g => g.Min(s => vlr[str.IndexOf(s)]));
var _str = group.Select(g => g.OrderBy(s => vlr[str.IndexOf(s)]).First());
and the result:

Categories