Finding a permutation from list<int> using linq - c#

This seems like it would be easy, but I have bumped into one issue after another... I have a search algorithm that requires a permutation search. I have a sql database that serves up to a web service. In my controller I have a complex search algorithm, one of the search options is to find permutations of a data field.
The data would be a list of integers like [0,0,9,3,8,7,4]... The search criterion would also come in as a list of integers like [9,4,7]... in this case the permutation exists, and it the evaluation should return true in the linq query.
So I have been attempting to write a "boolean" function that would return true or false to evaluate if a permutation exists... So far my attempts have worked except in the case where example 2 and 3 below both return true... because they are evaluated because of the single Zero, not that there are a quantity of two zeros... if that makes sense?
[0,0,9,3,8,7,4] => [9,4,7] = true
[0,0,9,3,8,7,4] => [0,0,2] = false
[0,0,9,3,8,7,4] => [9,0,0] = true
I have tried "GroupBy", which will give me a list of keys and counts, which should work... right? in #1 does the array contain 9,4, and 7... for the other two, does the array contain, two zeros and the remaining number... this is always evaluating to true but is always evaluating that the search string contains a single zero not two zeros...
Can anyone assist?

public bool IsPermutation(IEnumerable<int> list,IEnumerable<int> subList)
{
var grpListCnt = list.GroupBy(num => num).ToDictionary(grp => grp.Key, grp => grp.Count());
var subGroupCount = subList.GroupBy(num => num).ToDictionary(grp => grp.Key, grp => grp.Count());
foreach(var keypair in subGroupCount)
{
if (!(grpListCnt.ContainsKey(keypair.Key) &&
grpListCnt[keypair.Key] >= keypair.Value))
return false;
}
return true;
}

You could use simple Linq statement. Use Any and All extension and validate the combination.
// input
int[][] data = ...
int[] criteria = ...
bool matchFound = data.Any(x=> criteria.All(c=> x.Contains(c));
In case if your source data is one dimensional array, you could simply use nested part (as shown below)
// input
int[] data = ...
int[] criteria = ...
bool matchFound = criteria.All(c=> data.Contains(c));

Related

How to convert list of string into list of number then apply OrderByDescending on that list in C#

Consider this as the values in column Emp_code.
E1000
E1001
E9000
E4000
E1339
E10000
I'm using this code to first remove the E from all of the occurrences than convert them into number than apply OrderByDescending to the list.
var idd = db?.HRMS_EmpMst_TR?.Where(a => a.Emp_code != null)?
.Select(x=>x.Emp_code.Remove(0,1)).Select(int.Parse).OrderByDescending(y => y).First();
Can somebody help me with this code. I want to get 10000 as the answer.
Thanks for the help!
You need to
Use TrimStart('E') to remove E char from each string and parse it to
integer.
Get Max value from the processed sequence.
var input = new List<string>(){"E1000", "E1001", "E9000", "E4000", "E1339"};
var result = input
.Select(x => int.Parse(x.TrimStart('E'))) //Remove E and then parse string to integer
.Max(); //Get max value from an IEnumerable
Try Online: .NET Fiddle
You didn't say so, but I think you are working with a database, so you are working IQueryable, and not IEnumerable. This means that you can't use methods like String.TrimStart nor String.Parse.
So you have something called db, of which you didn't bother to tell us what it is. I assume it is a DbContext or something similar to access a database management system.
This DbContext has a table HRMS_EmpMst_TR, filled with rows of which I don't know what they are (please, next time give us some more information!). What I do know, that there are no null rows in this table. So your Where is meaningless.
By the way, are you not certain that db is not null?
if (db == null) return null;
After this, we know that db.HRMS_EmpMst_TR is a non null possible empty sequence of rows, where every row has a string column EmpCode. Every EmpCode starts with the character E followed by a four digits number. You want the EmpCode with the largest number.
string largestEmpCode = db.HRMS_EmpMst_TR
.OrderByDescending(row => row.EmpCode)
.Select(row => row.EmpCode)
.FirstOrDefault();
You get the string E9000, or null, if the table is empty. If you want 9000 just remove the first character and parse. What do you want if the table is empty?
if (largestEmpCode != null)
{
int largestEmpCodeValue = Int32.Parse(largestEmpCode.SubString(1));
}
else
{
// TODO: handle empty table.
}
There is room for improvement
If you are certain that every EmpCode is the character E followed by a four digit number, and you want to do calculations with this number, consider to change the EmpCode column to an integer column, without the E. This is a one time action, and it will make future calculations much easier.
Database column:
int EmpCodeValue;
LINQ to get the largest EmpCodeValue:
int largestEmpCodeValue = db.HRMS_EmpMst_TR
.Select(row => row.EmpCodeValue)
.Max();
If other parts of your application really need an "E followed by four digits", you can always make an extension method. I don't know what HRMS_EmpMst_TR are, let's assume it is a table of EmpMst
public string GetEmpCode(this EmpMst empMst)
{
return String.Format("E{0,04}", empMst.EmpCode);
}
I'm not sure about the ,04 part. You'll have to look it up, how to convert integer 4 to string "0004"
Usage:
List<EmpMst> fetchedEmpMsts = ...
string firstEmpCode = fetchedEmpMsts[0].GetEmpCode();
Or:
var result = db.HRMS_EmpMst_TR
.Where(empMst => empMst.Name == ...) // or use some other filter, just an example
.AsEnumerable()
.Select(empMst => new
{
Id = empMst.Id,
Name = empMst.Name,
EmpCode = empMst.GetEmpCode(),
...
});

Lambda Function to find most popular word in a List C# [duplicate]

This question already has answers here:
How to Count Duplicates in List with LINQ
(7 answers)
Closed 2 years ago.
I currently have what I believe is a lambda function with C# (fairly new to coding & haven't used a lambda function before so go easy), which adds duplicate strings (From FilteredList) in a list and counts the number of occurrences and stores that value in count. I only want the most used word from the list which I've managed to do by the "groups.OrderBy()... etc) line, however I'm pretty sure that I've made this very complicated for myself and very inefficient. As well as by adding the dictionary and the key value pairs.
var groups =
from s in FilteredList
group s by s into g
// orderby g descending
select new
{
Stuff = g.Key,
Count = g.Count()
};
groups = groups.OrderBy(g => g.Count).Reverse().Take(1);
var dictionary = groups.ToDictionary(g => g.Stuff, g => g.Count);
foreach (KeyValuePair<string, int> kvp in dictionary)
{
Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
}
Would someone please either help me through this and explain a little bit of this too me or at least point me in the direction of some learning materials which may help me better understand this.
For extra info: The FilteredList comes from a large piece of external text, read into a List of strings (split by delimiters), minus a list of string stop words.
Also, if this is not a lambda function or I've got any of the info in here incorrect, please kindly correct me so I can fix the question to be more relevant & help me find an answer.
Thanks in advance.
Yes, I think you have overcomplicated it somewhat.. Assuming your list of words is like:
var words = new[] { "what's", "the", "most", "most", "most", "mentioned", "word", "word" };
You can get the most mentioned word with:
words.GroupBy(w => w).OrderByDescending(g => g.Count()).First().Key;
Of course, you'd probably want to assign it to a variable, and presentationally you might want to break it into multiple lines:
var mostFrequentWord = words
.GroupBy(w => w) //make a list of sublists of words, like a dictionary of word:list<word>
.OrderByDescending(g => g.Count()) //order by sublist count descending
.First() //take the first list:sublist
.Key; //take the word
The GroupBy produces a collection of IGroupings, which is like a Dictionary<string, List<string>>. It maps each word (the key of the dictionary) to a list of all the occurrences of that word. In my example data, the IGrouping with the Key of "most" will be mapped to a List<string> of {"most","most","most"} which has the highest count of elements at 3. If we OrderByDescending the grouping based on the Count() of each of the lists then take the First, we'll get the IGrouping with a Key of "most", so all we need to do to retrieve the actual word is pull the Key out
If the word is just one of the properties of a larger object, then you can .GroupBy(o => o.Word). If you want some other property from the IGrouping such as its first or last then you can take that instead of the Key, but bear in mind that the property you end up taking might be different each time unless you enforce ordering of the list inside the grouping
If you want to make this more efficient than you can install MoreLinq and use MaxBy; getting the Max word By the count of the lists means you can avoid a sort operation. You could also avoid LINQ and use a dictionary:
string[] words = new[] { "what", "is", "the", "most", "most", "most", "mentioned", "word", "word" };
var maxK = "";
var maxV = -1;
var d = new Dictionary<string, int>();
foreach(var w in words){
if(!d.ContainsKey(w))
d[w] = 0;
d[w]++;
if(d[w] > maxV){
maxK = w;
maxV = d[w];
}
}
Console.WriteLine(maxK);
This keeps a dictionary that counts words as it goes, and will be more efficient than the LINQ route as it needs only a single pass of the word list, plus the associated dictionary lookups in contrast to "convert wordlist to list of sublists, sort list of sublists by sublist count, take first list item"
This should work:
var mostPopular = groups
.GroupBy(item => new {item.Stuff, item.Count})
.Select(g=> g.OrderByDescending(x=> x.Count).FirstOrDefault())
.ToList();
OrderByDescending along with .First() combines your usage of OrderBy, Reverse() and Take.
First part is a Linq operation to read the groups from the FilteredList.
var groups =
from s in FilteredList
group s by s into g
// orderby g descending
select new
{
Stuff = g.Key,
Count = g.Count()
};
The Lambda usage starts when the => signal is used. Basically means it's going to be computed at run time and an object of that type/format is to be created.
Example on your code:
groups = groups.OrderBy(g => g.Count).Reverse().Take(1);
Reading this, it is going to have an object 'g' that represents the elements on 'groups' with a property 'Count'. Being a list, it allows the 'Reverse' to be applied and the 'Take' to get the first element only.
As for documentation, best to search inside Stack Overflow, please check these links:
C# Lambda expressions: Why should I use them? - StackOverflow
Lambda Expressions in C# - external
Using a Lambda Expression Over a List in C# - external
Second step: if the data is coming from an external source and there are no performance issues, you can leave the code to refactor onwards. A more detail data analysis needs to be made to ensure another algorithm works.

Is there a dumb "all" in linq that evaluate all element?

Just like operator "&" will execute second condition even if first one is false,
I'm looking for the same thing for "All". I want the result of "All" but also want each elements to be evaluated. I called that a dumbAll in my example.
Here is an example of why I want this.
main
{
Object[] array = InitSomeArray();
AreObjectValid = array.dumbAll(o => Validate(o));
//other stuff with AreObjectValid
}
private bool Validate(object o)
{
bool IsValid = IsPositiveNumeric(o.ToString());
HighLighInvalidObjectWhenInvalid(o, IsInvalid: !IsValid);
return IsValid;
}
Does such thing exists in linq?
Note : I already know how to handle this without link.
You can use other query operators to achieve the same. One example is to count how many objects satisfy the condition.
Object[] array = InitSomeArray();
var count = array.Count(o => Validate(o));
var areObjectsValid = count == array.Length;
Not really, but you can achieve this aggregating the results.
var allValid = array.Aggregate(true, (acc, o) => acc & Validate(o));
I like this less than the Aggregate answer (and the Count answer as long as you have an array), but here:
AreObjectValid = array.Select(o => Validate(o)).ToArray().All(b => b);
or, as Asad points out,
AreObjectValid = array.Select(Validate).ToArray().All(b => b);
There's a bunch of LINQ answers here, but because I believe LINQ transformations should ideally be side effect free, here's an approach that uses a foreach and doesn't really sacrifice too much conciseness.
bool all = true;
foreach(var o in array)
all &= Validate(o);
This evaluates Validate for each value, and at the end all will be false if any of the values failed validation.

Search in part of the list by LINQ

I want to know if there's a way to search from a specific index in LINQ
This is what i want to do for example :
//search after the 4th element if exist any true
bool result = myList.Any(a=>a.flagTodo)
// in my case I want to do like: myList.where(a=>a.index >4).Any(a=>a.flagTodo)
there's any way to do it in the same linq ?
You can use Enumerable.Skip to bypass specified number of elements in sequence:
bool result = myList.Skip(4).Any(a => a.flagTodo);
BTW you don't need to verify list length - if there is less than 4 items, result will be false - Skip(4) will return empty sequence. And Any returns false because none of elements in sequence satisfies condition (sequence is empty).
You can skip the first 4 elements.
bool result = myList.Skip(4).Any(a => a.flagTodo);
But you need to check on your list length first before calling this statement.
Either use Skip as others have mentioned or pass the index so that you can use it in Any
bool result = myList.Select((obj, index) => new { obj, index })
.Any(x => x.index > 4 && x.obj.flagTodo);

Return Modal Average in LINQ (Mode)

I am not sure if CopyMost is the correct term to use here, but it's the term my client used ("CopyMost Data Protocol"). Sounds like he wants the mode? I have a set of data:
Increment Value
.02 1
.04 1
.06 1
.08 2
.10 2
I need to return which Value occurs the most "CopyMost". In this case, the value is 1. Right now I had planned on writing an Extension Method for IEnumerable to do this for integer values. Is there something built into Linq that already does this easily? Or is it best for me to write an extension method that would look something like this
records.CopyMost(x => x.Value);
EDIT
Looks like I am looking for the modal average. I've provided an updated answer that allows for a tiebreaker condition. It's meant to be used like this, and is generic.
records.CopyMost(x => x.Value, x => x == 0);
In this case x.Value would be an int, and if the the count of 0s was the same as the counts of 1s and 3s, it would tiebreak on 0.
Well, here's one option:
var query = (from item in data
group 1 by item.Value into g
orderby g.Count() descending
select g.Key).First();
Basically we're using GroupBy to group by the value - but all we're interested in for each group is the size of the group and the key (which is the original value). We sort the groups by size, and take the first element (the one with the most elements).
Does that help?
Jon beat me to it, but the term you're looking for is Modal Average.
Edit:
If I'm right In thinking that it's modal average you need then the following should do the trick:
var i = (from t in data
group t by t.Value into aggr
orderby aggr.Count() descending
select aggr.Key).First();
This method has been updated several times in my code over the years. It's become a very important method, and is much different than it use to be. I wanted to provide the most up to date version in case anyone was looking to add CopyMost or a Modal Average as a linq extension.
One thing I did not think I would need was a tiebreaker of some sort. I have now overloaded the method to include a tiebreaker.
public static K CopyMost<T, K>(this IEnumerable<T> records, Func<T, K> propertySelector, Func<K, bool> tieBreaker)
{
var grouped = records.GroupBy(x => propertySelector(x)).Select(x => new { Group = x, Count = x.Count() });
var maxCount = grouped.Max(x => x.Count);
var subGroup = grouped.Where(x => x.Count == maxCount);
if (subGroup.Count() == 1)
return subGroup.Single().Group.Key;
else
return subGroup.Where(x => tieBreaker(x.Group.Key)).Single().Group.Key;
}
The above assumes the user enters a legitimate tiebreaker condition. You may want to check and see if the tiebreaker returns a valid value, and if not, throw an exception. And here's my normal method.
public static K CopyMost<T, K>(this IEnumerable<T> records, Func<T, K> propertySelector)
{
return records.GroupBy(x => propertySelector(x)).OrderByDescending(x => x.Count()).Select(x => x.Key).First();
}

Categories