LINQ - Select min count - c#

I have a list of strings which contain X in them. I want to select list(s) with the minimum count of X in them. For example:
CountMin("AXBXX", "AAX") will return AAX.
How can I write this qith LINQ in a concise way ?

public static string CountMin(IList<string> inputList)
{
if (inputList == null || !inputList.Any()) return null;
var result = inputList.Select(s => new
{
Item = s,
Count => s.Count(ch => ch == 'X')
})
.OrderBy(item => item.Count).First().Item;
}
Snippet assumes that all elements on list are different to null. If you need it, it could be easily improved.
You can also omit temporary class:
inputList.OrderBy(s => s.Count(c => c == 'X')).First();

string[] list = {"AXBXX", "AAX", "AXX"};
string result = (from word in list
select new { word, wordLen = (word.Length - (word.Replace("X", "")).Length) })
.OrderBy(x => x.wordLen).First().word;
MessageBox.Show(result);

Here's an answer that will get you all of the minimum X strings from the list.
var listOfStrings = new List<string>()
{
"AXB",
"ABXXC",
"ABX",
};
var minimumXs =
listOfStrings
.GroupBy(x => x.Count(y => y == 'X'))
.OrderBy(x => x.Key)
.Take(1)
.SelectMany(x => x);
That gives me:
AXB
ABX

Related

C#:How to get the longest common prefix from a list of strings LINQ expression

I am trying to learn LINQ
I would like to understand how to get the longest common prefix from a list of strings
{"a","abC","abcD"}
would return "ab". Common as in at least 1 other string has it. Even though "a" is common for all 3, I would like to get "ab" because 2 elements share this prefix and "ab" is longer than "a"
It was an interesting challenge and this is my solution:
var array = new []{"a","abC","abcD"};
var longestCommonPrefix = Enumerable.Range(1, array.Max(_ => _)!.Length)
.Select(i =>
{
var grouped = array.Where(x => x.Length >= i)
.GroupBy(x => x[..i])
.Where(x => x.Count() > 1)
.OrderByDescending(x => x.Count())
.Select(x => new { LongestCommonPrefix = x.Key })
.FirstOrDefault();
return grouped?.LongestCommonPrefix ?? string.Empty;
}).Max();
var longestCommonPrefix = (words.FirstOrDefault() ?? String.Empty)
.Substring(0,
Enumerable.Range(0, words.Any() ? words.Min(x => x.Length) + 1 : 0)
.Where(x => words.Select(w => w.Substring(0, x))
.Distinct().Count() == 1).DefaultIfEmpty(0).Max()
);

Get Max occurence of element in Linq

Hello all i have 2 class for the example i will name it A and B
A is a list and every A element have a list of B element.
B element have a type
I want to get a B element by occurance of it in my list of B element.
var listB = A
.SelectMany(a => a.B);
var listBId = listB
.Where(b => b.Type == SelectedType)
.Select(b => b.Id);
var IdMaxoccur = listBId
.GroupBy(x => x)
.OrderByDescending(x => x.Count())
.First()
.Key;
I find this a bit heavy for just get the max occurence of an id in a list....
Do you know a better way to do this?
I think your code is good enough if you rewrite and simplify it a bit and handle case when your list is empty. This code assumes that 0 is not a valid Id.
var result = A
.SelectMany(x => x.B)
.Where(x => x.Type == selectedType)
.GroupBy(x => x.Id, new { Id = x.Key, Count = x.Count() })
.OrderByDescending(x => x.Count)
.FirstOrDefault();
Console.WriteLine("Max ID = {0}, Count = {1}", result.Id, result.Count);
If you still think that your existing code is too complex, you could write extension method to hide complexity
public static int TryGetBIdWithMaxOccur(this IEnumerable<A> input, SelectedTypeEnum selectedType)
{
var result = input
.SelectMany(x => x.B)
.Where(x => x.Type == selectedType)
.GroupBy(x => x.Id, new { Id = x.Key, Count = x.Count() })
.OrderByDescending(x => x.Count)
.Select(x => x.Id)
.FirstOrDefault();
return result;
}
Then you can use it like this:
var result = A.TryGetBIdWithMaxOccur(SelectedTypeEnum.CoolValue);
if(result != default(int))
{
//do stuff
}
just an idea
var bs = A.SelectMany().Where().Select(b=>b.Id).OrderBy();
int current = -1, maxB = -1; // make sure it is stub Id
int currentCount = 0, maxCount = 0;
foreach(var b in bs)
{
if (b != current)
{
// check if previous was max
if (currentCount > maxCount)
{
maxB = current;
maxCount = currentCount;
}
// change current
current = b;
currentCount = 0;
}
currentCount ++;
}
To make it a bit shorter you can put the Where selector inside SelectMany and use GroupBy overload:
var idMaxOccur = A
.SelectMany(a => a.B.Where(b => b.Type == selectedType))
.GroupBy(b => b.Id, b => b.Id)
.OrderByDescending(g => g.Count())
.First().Key;

Select lists from lists with linq

I have an list with x items. I wish to get an results that groups this list based of a number and not a property.
For example.
I have a list of 8 items. I want to group them by 3.
I want to get a List thats contains three lists, where the first two lists contains each three items and the last list the remaining two.
I want a more elegant solution than this:
private static List<List<string>> GroupBy(List<string> pages, int groupSize)
{
var result = new List<List<TrimlinePage>>();
while (!(result.Count != 0 && result.Last().Count % 3 > 0))
{
int skip = result.Count*groupSize;
var group = pages.Skip(skip).Take(groupSize).ToList();
result.Add(group);
}
return result;
}
You can use the integer divison trick:
List<List<string>> lists = pages
.Select((str, index) => new { str, index })
.GroupBy(x => x.index / groupSize)
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Example:
int groupSize = 3;
var pages = new List<string> { "A", "B", "C", "D", "E", "F", "G" };
List<List<string>> lists = pages
.Select((str, index) => new { str, index })
.GroupBy(x => x.index / groupSize)
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Result:
foreach(var list in lists)
Console.WriteLine(string.Join(",", list));
Output:
A,B,C
D,E,F
G
So this approach will give you lists with the specified max-size, in this case 3. If you instead want to ensure that you always get three lists you need to use % instead of /:
List<List<string>> lists = pages
.Select((str, index) => new { str, index })
.GroupBy(x => x.index % groupSize)
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Try this:
var list = Enumerable.Range(1,100);
var query = list
.Select((x, i) => new {x, i})
.GroupBy(v => v.i / 3).Select(g => g.Select(v =>v.x.ToList()))
.ToList();
Here's a simple solution using side effects (which is generally discouraged):
private static List<List<string>> GroupBy(List<string> pages, int groupSize)
{
var i = 0;
return pages.GroupBy(p => i++ / 3, (k, g) => g.ToList()).ToList();
}
Or if you want to avoid relying on side effects, you could use this:
private static List<List<string>> GroupBy(List<string> pages, int groupSize)
{
return pages.Select(p => new { p, i })
.GroupBy(x => x.i / 3)
.Select(g => g.Select(x => x.p).ToList())
.ToList();
}
LINQ is not the best solution. Often good old indexing is much more readable and efficient.
private static List<List<T>> GroupBy(List<T> pages, int groupSize)
{
var result = new List<List<T>>();
List<T> l;
for (int i=0; i < pages.Count; i++)
{
if (i%groupSize == 0)
{
l = new List<T>();
result.Add(l);
}
l.Add(pages[i]);
}
return result;
}
You could also have a look at morelinq which contains the Partition method.
It's available via NuGet.

Find multiple index in array

Say I have an array like this
string [] fruits = {"watermelon","apple","apple","kiwi","pear","banana"};
Is there an built in function that allows me to query all the index of "apple" ?
For example,
fruits.FindAllIndex("apple");
will return an array of 1 and 2
If there is not, how should I implement it?
Thanks!
LINQ version:
var indexes = fruits.Select((value, index) => new { value, index })
.Where(x => x.value == "apple")
.Select(x => x.index)
.ToList();
Non-LINQ version, using Array<T>.IndexOf() static method:
var indexes = new List<int>();
var lastIndex = 0;
while ((lastIndex = Array.IndexOf(fruits, "apple", lastIndex)) != -1)
{
indexes.Add(lastIndex);
lastIndex++;
}
One way would be to write like this:
var indices = fruits
.Select ((f, i) => new {f, i})
.Where (x => x.f == "apple")
.Select (x => x.i);
Or the traditional way:
var indices = new List<int>();
for (int i = 0; i < fruits.Length; i++)
if(fruits[i] == "apple")
indices.Add(i);
Pretty easy with an extension method.
var fruits = new[] { "watermelon","apple","apple","kiwi","pear","banana" };
var indexes = fruits.FindAllIndexes("apple");
public static class Extensions
{
public static int[] FindAllIndexes(this string[] array, string search) => array
.Select((x, i) => (x, i))
.Where(value => value.x == search)
.Select(value => value.i)
.ToArray();
}

Get index of lines that match my linq

I have a linq statement and I would like to know if it is possible to get indicies of lines that match my statement? Here it is:
var result = list3.Where(middle => list4.Any(x => x == middle.Middle.category1)).Select(obj => new { obj, dt = DateTime.ParseExact(obj.LeftColumn, dateFormat, CultureInfo.InvariantCulture) })
.Where(x => x.dt >= datetimepickerChoice1 && x.dt <= datetimepickerChoice2)
.Select(x => x.obj).ToList();
You can use the overload of Select (or Where) which projects also the index of the element:
var result = list3.Select((middle, index) => new{ middle, index })
.Where(x => list4.Any(xx => xx == x.middle.Middle.category1))
.Select(x => new { x.middle, x.index, dt = DateTime.ParseExact(x.middle.LeftColumn, dateFormat, CultureInfo.InvariantCulture) })
.Where(x => x.dt >= czas11 && x.dt <= czas22)
.Select(x => x.index)
.ToList();
Side-note: consider to change your variable names to be more meaningful. That is unreadable.
do you mean this?
var result = list3.Where(middle => list4.Any(x => x == middle.Middle.category1))
.Select(obj => new { obj, dt = DateTime.ParseExact(obj.LeftColumn, dateFormat, CultureInfo.InvariantCulture) })
.Where(x => x.dt >= czas11 && x.dt <= czas22)
.Select((x,index) =>new{ x.obj,Index=index}).ToList();
Also note that if you want to search for the indicies of items matching a predicate very often, it could be worth writing a very simple extension method:
public static class IEnumerableExt
{
public static IEnumerable<int> FindIndices<T>(this IEnumerable<T> self, Predicate<T> predicate)
{
int i = 0;
foreach (var element in self)
{
if (predicate(element))
yield return i;
++i;
}
}
}
Which you would call like this:
var result = list3.FindIndices(x => list4.Any(xx => xx == x.middle.Middle.category1));

Categories

Resources