USe LINQ to remove from a list based on conditions - c#

I have a list 'nList' returned from a LINQ query. The data is correct but I have to check if the data in the 2nd field ('nType') occurs more than once, and if it does, remove it from the list if the 3rd field ('nAmount') is null.
To illustrate, in the example below, I would want to remove the record with ID 2.
Can this be done using LINQ?
EDIT: There can only be 2 scenarios:
one occurrence of nType, with nAmount populated
two occurences of nType, with one nAmount populated, the other nAmount null
ID nType nAmount
1 A 12.00
2 A
3 B 13.00
4 C 14.00

var result = nList.Where(x => x.nAmount != null).DistinctBy(x => x.nType);
You would need to install MoreLinq from Nuget to get the DistinctBy().

This can be done with a GroupBy overload which lets you define the grouped object
var result = nList.GroupBy(
x => x.nType,
(key,g) => new {
ID = g.First(x => x.nAmount != null).ID,
nType = key,
nAmount = g.First(x => x.nAmount != null).nAmount }
);
Live example: http://rextester.com/PFX41986
This has a few caveats
If your resultset has a single nType which has a null nAmount it will not appear in the results
If you get more than 1 distinct nType with a non-null nAmount this will take the first one.

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(),
...
});

Linq query “where [column] in (list of values)” that return single value for each values?

Let say we have this LinQ code (in LinqPad):
List<string> list = new List<string>();
list.Add("1611080010");
list.Add("1611080011");
list.Add("WRONGID");
var result = Orders.AsQueryable().Where(y => list.Contains(y.Id));
// And yes my Ids are string for this sample
result.Dump(); // To display the result in LinqPad
result.Count(); // equal 2
Is it possible to improve this query to force the system to return one element for each of my list element or throw an exception? So if I have 3 values in my list I should have 3 values in my result?
On your question: Is it possible to improve this query to force the system to return one element for each of my list element or throw an exception:
var result = Orders.AsQueryable().SingleOrDefault(y => list.Contains(y.Id));
SingleOrDefault
Returns the only element of a sequence, or a default value if the
sequence is empty; this method throws an exception if there is more
than one element in the sequence.
You can use any one from below one
Whenever you use SingleOrDefault, you clearly state that the query should result in at most a single result. On the other hand, when FirstOrDefault is used, the query can return any amount of results but you state that you only want the first one.
I personally find the semantics very different and using the appropriate one, depending on the expected results, improves readability.
var result = Orders.AsQueryable().SingleOrDefault(y => list.Contains(y.Id));
var result = Orders.AsQueryable()..FirstOrDefault(y => list.Contains(y.Id));
If I understand your question correctly you want to return one object from Orders where the Id of the object equals an Id in your list and if an Id in the list is not present in the Orders object an exception should be thrown.
To achieve that you could do:
var result = list.Select(id => Orders.AsQueryable().First(y => y.Id == id)).ToList();
This will throw an exception if an Id from the list has no match in the Orders object. If all Ids are found then your result will contain the same number of elements as there are Ids in the list.
Another option would be to use:
var result = list.Select(id => Orders.AsQueryable().FirstOrDefault(y => y.Id == id)).ToList();
This would not throw an error, but it would always return the same number of elements as there are Ids in the list. The Ids that would not be found would have a null entry though.

Distinct the collection in Linq C#

I have the collection
From the collection the column name ma_position_value should not contain the same value
For Example
If the collection have 5 records, The column ma_position_value should not contain the same value from all 5 records...but it can contain same value for 2 or 3 or 4 records from the collection Atleast one column value should change.
So the main intension is ALL 5 records should not contain same value.Any one should get different value.So if all 5 is same I tried to throw a message
So I have just write a bool to return it if it is change
bool lblMa = false;
lblMa = ibusCalcWiz.iclbMssPaServiceSummary
.Where(lbusMssPaServiceSummary => lbusMssPaServiceSummary.icdoSummary.ma_position_value.IsNotNullOrEmpty()).Distinct().Count() > 1;
But it is always return true.
Just select distinct ma_position_value property values:
bool allSame = ibusCalcWiz.iclbMssPaServiceSummary
.Select(i => i.ma_position_value)
.Distinct()
.Count() == 1;
HINT: Do not use long variable names in lambda expressions. There is a rule of thumb - the bigger scope of variable usage, the bigger should be name. If scope is very small (lambda expression) then name should be very small (single letter is enough).
You can get the first one, and check if they are all equal:
// Only works if the collection is non-empty!
string first_ma_position_value = ibusCalcWiz.iclbMssPaServiceSummary.First().ma_position_value;
bool allTheSame = ibusCalcWiz.iclbMssPaServiceSummary
.All(lbusMssPaServiceSummary.icdoSummary.ma_position_value == first_ma_position_value);
or you can do the distinct, as you originally wanted, but on the value of the column instead of on the objects
bool allTheSame = ibusCalcWiz.iclbMssPaServiceSummary
.Select(lbusMssPaServiceSummary => lbusMssPaServiceSummary.icdoSummary.ma_position_value)
.Distinct()
.Count() == 1;

To apply mulitple criteria in lambda expression in c#

I have two main tables Listings and Place . In listing table there is a field PlaceId which referes to a Place entity/row/object . I want to query on both tables so that i get both of them like this .
var query = context.Listings
.Include("Place")
.Where(l => l.Place.TypeId == Type.Ro)
.OrderBy(l => l.Id).ToList();
after this now i want to put some filter on this query , here is the condition .
i got only a string like this var filter = "1,2,4"; . Now i want to filter on listing to gett all these listing where bedroom is equal to 1 OR 2 OR 4 .
What i have done
string minBeds = "1,2,4";
foreach (var item in minBeds.Split(','))
{
int minBed = int.Parse(item);
query = query.Where(l=>l.Place.Bedroom == minBed).ToList();
}
But doing this is giving me Zero result.
The problem with the way you're filtering it. After the first pass, you're filtering out everything except where Bedroom == 1, on the second pass you're filtering out everything except where Bedroom == 2, but since the only items in the list have Bedroom == 1, you won't have anything in the result set.
The solution is to use the conventional C# || operator:
query = query.Where(l => l.Place.Bedroom == "1" ||
l.Place.Bedroom == "2" ||
l.Place.Bedroom == "4");
Or if you want to be more flexible, use the Contains method:
string[] minBeds = "1,2,4".Split(',');
query = query.Where(l => minBeds.Contains(l.Place.Bedroom));
Note if Bedroom is an integer, you'll need to convert the input to an appropriate type first:
var minBeds = "1,2,4".Split(',').Select(int.Parse);
query = query.Where(l => minBeds.Contains(l.Place.Bedroom));
Also note, I've eliminated the ToList here. Unless you need to access items by index and add / remove items from the result collection, it's most likely just a waste of resources. You can usually rely on Linq's native laziness to delay processing to query until you really need the result.

Linq Union: How to add a literal value to the query?

I need to add a literal value to a query. My attempt
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
In the above example, I get an error:
"Local sequence cannot be used in LINQ to SQL implementation
of query operators except the Contains() operator."
If I am using Entity Framework 4 for example, what could I add to the Union statement to always include the "seed" ID?
I am trying to produce SQL code like the following:
select distinct ID
from product
union
select 0 as ID
So later I can join the list to itself so I can find all values where the next highest value is not present (finding the lowest available ID in the set).
Edit: Original Linq Query to find lowest available ID
var skuQuery = Context.Products
.Where(p => p.sku > skuSeedStart &&
p.sku < skuSeedEnd)
.Select(p => p.sku).Distinct();
var lowestSkuAvailableList =
(from p1 in skuQuery
from p2 in skuQuery.Where(a => a == p1 + 1).DefaultIfEmpty()
where p2 == 0 // zero is default for long where it would be null
select p1).ToList();
var Answer = (lowestSkuAvailableList.Count == 0
? skuSeedStart :
lowestSkuAvailableList.Min()) + 1;
This code creates two SKU sets offset by one, then selects the SKU where the next highest doesn't exist. Afterward, it selects the minimum of that (lowest SKU where next highest is available).
For this to work, the seed must be in the set joined together.
Your problem is that your query is being turned entirely into a LINQ-to-SQL query, when what you need is a LINQ-to-SQL query with local manipulation on top of it.
The solution is to tell the compiler that you want to use LINQ-to-Objects after processing the query (in other words, change the extension method resolution to look at IEnumerable<T>, not IQueryable<T>). The easiest way to do this is to tack AsEnumerable() onto the end of your query, like so:
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().AsEnumerable().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
Up front: not answering exactly the question you asked, but solving your problem in a different way.
How about this:
var a = Products.Select(p => p.sku).Distinct().ToList();
a.Add(0);
a.Dump(); // LinqPad's way of showing the values
You should create database table for storing constant values and pass query from this table to Union operator.
For example, let's imagine table "Defaults" with fields "Name" and "Value" with only one record ("SKU", 0).
Then you can rewrite your expression like this:
var zero = context.Defaults.Where(_=>_.Name == "SKU").Select(_=>_.Value);
var result = context.Products.Select(p => p.sku).Distinct().Union(zero).ToList();

Categories