SubQuery using Lambda Expression - c#

I am using a LINQ subquery to obtain all the words of minimum length in an array.
I want to do it using Lambda Expression.
var names = new[] { "Tom", "Dick", "Harry", "Mary", "Jay" }.AsQueryable();
(
from n in names
where n.Length == names.Min (n2 => n2.Length)
select n
)
Output :
Tom , Jay
Thanks,
Prakhar

This would work:
var minNames = names.Where(s => s.Length == names.Min(n=>n.Length));
But it evaluates the min length for every name in list (O(n*n) complexity), therefore this would be better:
var min = names.Min(s => s.Length); //calc. this only once
var minNames = names.Where(s => s.Length == min);

The question to me seems a little bit vague, but is this what you're looking for?
names.Where (x => x.Length == names.Min (n2 => n2.Length));

This should help you:
var minNames = names.Where(c => c.Length == names.Min(n => n.Length))
.ToArray();

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()
);

String Operation using Linq

I am trying to convert FOO BAR BAZ to "GPCSC[", "N##" using linq.
I came this close:
var res2 = new String("FOO BAR BAZ ".ToList().Select((x, i) => x = (i % 2 == 0 ? ++x : --x )).ToArray());
which outputs
GNPC#SC#[
I need to split this string into two parts so that my output will be
`"GPCSC[", "N##"`
I couldn't sort it out yet.
Any solution advice on this?
PS: I am looking for simple solution, not bunch of lines of codes
Fiddle Link : https://dotnetfiddle.net/ml8bOC
You might need to use GroupBy:
string str = "GNPC#SC#[";
var groups = str.Select((v, i) => new { Group = i % 3, Ch = v })
.GroupBy(item => item.Group == 1)
.Select(group => string.Join("", group.Select(item => item.Ch)))
.ToList();
// groups: ["GPCSC[", "N##"]

Split each two-word string in a list, compare if same and count using Linq

Is it possible to split each string (containing 2 words) in a list, then compare if both words are the same and count that occurrences using Linq? For example:
Let's say I have a list containing
list[0] = "bla bla";
list[1] = "bla heh";
list[2] = "heh heh";
The output of count should be 2 in this case.
my attempt so far:
var count = lst.Count(c => c.Split(.......)....
can't get past this.
Any help would be greatly appreciated.
list.Select(c => c.Split(' ')).Count(y => y.Length >= 2 && y[0] == y[1]);
You can utilise the Select clause then the Count like so:
int count = myList.Select(s => s.Split(' '))
.Count(a => a[0] == a[1]);
or you can use Count only like this:
int count = myList.Count(s => s.Substring(0, s.IndexOf(' ')) ==
s.Substring(s.IndexOf(' ') + 1));
With the new Span<T> value type (see this article) from nuget package 'System.Memory' you can do this without any unnecessary allocations:
int count = input.Count(x =>
{
int index = x.IndexOf(' ');
if (index < 1 || index == x.Length - 1) return false;
var span = x.AsSpan();
return span.Slice(start:0, length:index) // no allocation
.SequenceEqual(
span.Slice(start: index + 1)); // no allocation
});
Same as the others, but if you would have 3 or more words it would check them against eachother and only count the arrays that have the same words everywhere.
var result = test.Select(x => x.Split(' ')).Count(x => x.All(y => x[0] == y));

Filtering a group by linq query

var testingAll = (from ac in metaData.AcTable
where ac.Call >= DateTime.Now.AddMonths(-2) && ac.Call <= DateTime.Now
group adminCall by ac.LanguageCode into acc
select new { lang = acc.Key, count = acc.Count() }).ToDictionary(x => x.lang, y => y.count).OrderByDescending(x => x.Key);
Can I have filter again after the datetime ?
Something like this:
var Today = testingAll.Where( /*x => x.Call >= DateTime.Now.AddDays(-2)*/)
I think you want something like
var testingAll = (from ac in metaData.AcTable
where ac.Call >= DateTime.Now.AddMonths(-2) && ac.Call <= DateTime.Now
group adminCall by adminCall.LanguageCode into ac
select ac
this should give back a collection where you can then query a number of times.
The short answer is that you can't do this. Think of it this way, the problem is effectively the same as me giving you the average age of children in a class and then you taking that number and trying to work out the average age of the boys only - it's not possible without the source data.
Now you could maybe do this by building an expression and spending a lot of effort there, but it would still have to re-query the database.
If you really want to abstract it away slightly, then you could create a function that takes the where predicate as a parameter:
public IEnumerable<KeyValuePair<string, int>> GetFilteredCalls(
Expression<Func<Call, bool>> predicate)
{
return calls
.Where(predicate)
.GroupBy(c => c.LanguageCode)
.Select(g => new { Lang = g.Key, Count = g.Count() })
.ToDictionary(x => x.Lang, y => y.Count)
.OrderByDescending(x => x.Key);
}
And you use it like this:
var calls = GetFilteredCalls(c => c.Call >= DateTime.Now.AddMonths(-2)
&& c.Call <= DateTime.Now);
var moreCalls = GetFilteredCalls(c => c.Call >= DateTime.Now.AddDays(-2)
&& c.Call <= DateTime.Now);

LINQ orderby from specific value

Sample:
var aux = new int[] { -1,0,1,-1,2,3,4,5,6,7 }
Expected result:
{ 2,3,4,5,6,7,1,0,-1,-1 }
How?
--
Edit: Sorry for the poor question.
I want to order all from the value 2, and others who are put in the lower end (I fixed the text).
var query = aux
.OrderBy(i => i < 2 ? 2 : 1) //small numbers last
.ThenBy(i => i < 2 ? -i : i); //large numbers asc, small numbers desc
var res = aux.Where(a => a>=2 ).OrderBy (a => a)
.Concat(aux.Where (a => a <2).OrderByDescending (a => a))

Categories

Resources