Conditional statement inside WHERE clause - c#

Consider the following records:
The outlined records are the same gameobject but they have different configuration names (the record where ConfigurationName is null is a copy from my original table).
string customName= "Config1";
string draftName = "Draft_" + id;
var list = db.JoinedGameObjects
.Where(x => x.TBUploads_ID == id)
.Where(x => x.ConfigurationName.Equals(customName) || x.ConfigurationName == null)
.Select(x => new GameObjectViewModel()
{
ID = x.ID,
GameObjectName = x.GameObjectName,
Category = x.Category,
Family = x.Family,
Type = x.Type,
MetaTypeJson = x.MetaTypeJson,
MaterialsJson = x.MaterialsJson,
MetadataVisibilityJson = x.MetadataVisibilityJson,
Visible = x.joined_Visible ?? x.Visible
}).ToList();
This code gets all records where ConfigurationName equals my customName variable or where ConfigurationName = null. However, this is not what I want.
I'll try to explain the desired outcome with an example.
I first want to check if there is a record with this ID with ConfigurationName equal to draftName (if true select this record), if not check if there is a record with this ID with ConfigurationName equal to customName (if true select this gameobject), if not take the record with this ID where ConfigurationName is null.
Can someone guide me in the right direction or provide a working example?

It seems like you already have a working concept, just add a draftName comparison to your Where condition.
It will apply conditions in order defined in Where function
.Where(x => x.ConfigurationName.Equals(draftName) || x.ConfigurationName.Equals(customName) || x.ConfigurationName == null)
Edit #1:
This should reflect what the OP wants.
For the sake of brevity, I used custom `Person` class and simplified the code example, so the concept should be easy to understand:
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
Then the rest:
var people = new List<Person>
{
new Person {Id= 1, Name = null},
new Person {Id= 1, Name = "Mike"},
new Person {Id= 1, Name = "John"},
new Person{Id = 2, Name= null},
new Person{Id = 2, Name= "Peter"},
};
//filter values
string filter1 = "Mike";
string filter2 = "John";
string filter3 = null;
var byPriority = people
.Where(p=> p.Name == filter1 || p.Name == filter2 || p.Name == filter3)
.OrderByDescending(p => p.Name == filter1)
.ThenByDescending(p => p.Name == filter2)
.ThenByDescending(p => p.Name == filter3);
var oneByIId = byPriority
.GroupBy(p => p.Id)
.Select(g => g.First())
.ToList();
The key here is to order the records by conditions priority. Then group the records and take the first one from each group
Edit #2
In some cases, LINQ to SQL may fail when comparing to `NULL`, in such a case, modify null comparing parts as this:
var byPriority = people
.Where(p=> p.Name == filter1 || p.Name == filter2 || object.Equals(p.Name,filter3))
.OrderByDescending(p => p.Name == filter1)
.ThenByDescending(p => p.Name == filter2)
.ThenByDescending(p => object.Equals(p.Name,filter3));
var oneByIId = byPriority
.GroupBy(p => p.Id)
.Select(g => g.First())
.ToList();

Related

Aggregate of string on anonymous type

I want to concat multiple string value into single string with comma separated,i tried using aggregate function but it shows cannot convert string to how to fix this issue,
I tried below code
var res = (from e in WYNKContext.SurgeryAssigned.Where(x => x.CmpID == cmpid && x.IsCancelled == false)
select new
{
ID = e.SAID,
UIN = e.UIN,
SurgeryDate = e.SurgeryDate,
SurgeryID = e.SurgeryID,
Surgery = ((from st in WYNKContext.SurgeryTran.
Where(x => x.SurgeryID == e.SurgeryID)
select new
{
desc = icdmaster
.Where(x => x.ID ==
st.IcdSpecialityCode).Select(x =>
x.SpecialityDescription).FirstOrDefault(),
}).ToList()).Aggregate((a, b) => a.desc + "," + b.desc),
}).ToList();
I want Output like inside surgery property like = string1,string 2 ,etc....
without using aggregate i am getting as count in Surgery Property
var res = (from e in WYNKContext.SurgeryAssigned.Where(x => x.CmpID == cmpid && x.IsCancelled == false)
select new
{
ID = e.SAID,
UIN = e.UIN,
SurgeryDate = e.SurgeryDate,
SurgeryID = e.SurgeryID,
Surgery = (from st in WYNKContext.SurgeryTran.Where(x => x.SurgeryID == e.SurgeryID)
select new
{
icd = icdmaster.Where(x => x.ID == st.IcdSpecialityCode).Select(x => x.SpecialityDescription).FirstOrDefault(),
}).ToList(),
}).ToList();
also tried string join :
Surgery = string.Join(",", (from st in WYNKContext.SurgeryTran.Where(x => x.SurgeryID == e.SurgeryID)
select new
{
icd = icdmaster.Where(x => x.ID == st.IcdSpecialityCode).Select(x => x.SpecialityDescription).FirstOrDefault(),
}).ToList()),
but in output i am getting like this
Surgery ={ icd = CORNEA },{ icd = CATARACT/IOL }
can some one tell what i did wrong in string.join.....
The string class has a static method named Join, which takes in a collection of items and a string to join them with, which should work for you here.
If I'm reading your code correctly, it would look something like this:
Surgery = string.Join(",", WYNKContext.SurgeryTran
.Where(surgTran => surgTran.SurgeryID == e.SurgeryID)
.Select(surgTran => icdmaster
.Where(icd => icd.ID == surgTran.IcdSpecialityCode)
.Select(icd => icd.SpecialityDescription)
.FirstOrDefault())),

How to use if else statement inside select new

I have a list of FlightTicketRequestDocument used for both Patients and their Escorts requesting a plane ticket. Theses are grouped with a GUID named GroupId.
I'm trying to group them inside this object:
public class FlightTicketRequestDocumentGroup
{
public Guid GroupId { get; set; }
public FlightTicketRequestDocument PrincipalDocument { get; set; }
public List<FlightTicketRequestDocument> GroupDocuments { get; set; }
}
PrincipalDocument holds the Patient if there's one. (If there isn't we just want the first Escort)
GroupDocuments holds the Escorts. (Except the first one that is placed in PrincipalDocument)
I'm attempting to use different expressions to set my values inside a Select New statement depending if there's a Patient or not but I'm getting the error:
The nested query is not supported. Operation1='Case' Operation2='VarRef'
Here's the code (Note: StayEscortId is null when the person is a Patient.):
var indexGroups = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.Select(g => new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = g.Any(f => f.StayEscortId == null)
? g.FirstOrDefault(f => f.StayEscortId == null)
: g.FirstOrDefault(),
GroupDocuments = g.Any(f => f.StayEscortId == null)
? g.Where(c => c.StayEscortId != null).ToList()
: g.Where(c => c.StayEscortId != null).OrderBy(f => f.FullName).Skip(1).ToList()
})
.ToListAsync(cancellationToken);
What can I use instead of the conditional operator ?: ?
The solution that I ended up using:
As specified by #mjwills, since I simply want the values where StayEscordId is null first, all I need to do is to sort as a boolean (false values end up at the top).
For the rest of the group, I do the same operation while Skipping 1 (the first one I chose).
Modified code:
var indexGroups = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.Select(g => new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = g.OrderBy(f => f.StayEscortId != null).ThenBy(f => f.FullName).FirstOrDefault(),
GroupDocuments = g.OrderBy(f => f.StayEscortId != null).ThenBy(f => f.FullName).Skip(1).ToList()
})
.ToListAsync(cancellationToken);
Here's what the query would look like if you were to use if/else instead of the ternary conditions. You would just use a code block inside of the .Select() to return the results:
var flightTicketRequestDocuments = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.ToListAsync(cancellationToken);
var indexGroups = flightTicketRequestDocuments
.Select(g =>
{
FlightTicketRequestDocument principalDocument;
List<FlightTicketRequestDocument> groupDocuments;
if (g.Any(f => f.StayEscortId == null))
{
principalDocument = g.FirstOrDefault(f => f.StayEscortId == null);
groupDocuments = g.Where(c => c.StayEscortId != null).ToList();
}
else
{
principalDocument = g.FirstOrDefault();
groupDocuments = g.Where(c => c.StayEscortId != null).OrderBy(f => f.FullName).Skip(1).ToList();
}
return new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = principalDocument,
GroupDocuments = groupDocuments
};
})
.ToList();
Note that this approach requires enumerating the IQueryable before performing the .Select() so that it's applied against an IEnumerable.
Needs a check or two (or a tweak or two) but if i remember correctly expression trees accept null coalescence operator you may try to swap in the place of the ternary.
Starting (and BASING) from your example, maybe something like the following?
var indexGroups = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.Select(g => new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = g.FirstOrDefault(f => f.StayEscortId == null) ?? g.OrderBy(f => f.FullName).FirstOrDefault(),
GroupDocuments = g.Where(c => c.StayEscortId != null).OrderBy(f => f.FullName).Skip(Math.Max(x.Count(y => y.StayEscortId == null), 1)).ToList()
})
.ToListAsync(cancellationToken);

Improving speed of method after adding value from different table aspn.net

I've got two tables: Index and Codes
when one condition is true, I need to check whether the Index is still valid and for that I need to get EndDate of the code which is in Codes table (as I've got only id of code in Index table)
This is how I do that:
1) First I get all Codes that have ended already (approx 3k+ items)
var goods = _context.Codes.Select(a =>
new Codes
{
Loid = a.Loid,
Pid = a.Pid,
Code = a.Code,
Startdate = a.Startdate,
Enddate = a.Enddate
})
.Where(x => x.Enddate < DateTime.Now)
.ToList();
then I'm taking my Index and adding values there as well as CodeEnd from Codes list above:
query = _context.VpAbcIndex
.AsNoTracking()
.Select(e => new VpAbcIndex
{
Id = e.Id,
ParentName = e.ParentName ?? "!",
ParentEndDate = e.ParentEndDate,
ParentStartDate = e.ParentStartDate,
ParentCode = e.ParentCode,
ParentNote = e.ParentNote ?? "",
ParentStatus = e.ParentStatus,
ChildName = e.ChildName ?? "!",
ChildId = e.ChildId,
ChildEndDate = e.ChildEndDate,
ChildStartDate = e.ChildStartDate,
CodeEnd = (filter.Level == 0) ? goods
.FirstOrDefault( x => x.Code == e.ParentCode).Enddate :
(filter.Level == 1) ? goods
.FirstOrDefault(x => x.Code == e.ChildCode).Enddate :
(filter.Level == 2) ?
goods
.FirstOrDefault(x => x.Code == e.GrandChildCode).Enddate : null
})
;
}
That's approx 10k items. It doesn't seem that much however it takes quite a long time for them to appear in my browser. Am I doing something wrong? Is there a faster way to join these values?

Order list by parent and child items

I have a list of products that have to be ordered by parent then all children of that parent, then next parent, etc.
Product One
Child One
Child Two
Product Two
Child One
These products are all in one table with a parent id field, the child products have a parent id but the parent items can have a null parent (indicating that product is a top level product)
I was thinking something like the following:
var list = GetProductList();
var newList = new List<ProductDTO>();
var parents = from p in list
where p.Parent == null
select p.Id;
foreach (var parent in parents)
{
var tempList = new List<ProductDTO>();
tempList.Add(list.FirstOrDefault(x => x.Id == parent));
tempList.AddRange(list.Where(x => x.Parent == parent).OrderBy(x => x.Id));
newList.AddRange(tempList);
}
Any suggestions on how I would do this a little cleaner?
you could try something like that. Assuming parent is a nullable:
var sorted = list.OrderBy(x => x.parent ?? x.id).ThenBy(x=>x.id);
if its a string:
var sorted = list.OrderBy(x =>
{
if (x.parent == "null")
return x.id;
else
return Convert.ToInt32(x.parent);
}).ThenBy(x => x.id);
Given "Parent" is nullable property (assuming nullable int here). Following should give you parent-child related ordered list:
public class ProductDTO
{
public int Id { get; set; }
public string Name { get; set; }
public int? Parent { get; set; }
}
var products = new List<ProductDTO>();
products.Add(new ProductDTO() { Id = 1, Name = "Product One" });
products.Add(new ProductDTO() { Id = 2, Name = "Product Two" });
products.Add(new ProductDTO() { Id = 3, Name = "Product Three" });
products.Add(new ProductDTO() { Id = 4, Name = "Child One", Parent=1 });
products.Add(new ProductDTO() { Id = 5, Name = "Child Two", Parent = 2 });
products.Add(new ProductDTO() { Id = 6, Name = "Child One", Parent = 1 });
var ordered = products
.Where(p => p.Parent == null)
.OrderBy(p=> p.Id)
.Select(p => products
.Where(c => c.Parent == p.Id)
.OrderBy(c => c.Id))
.ToList();
You could do it this way:
list.ForEach(item =>
{
if (item.Parent == null)
{
orderedList.Add(item);
orderedList.AddRange(list.Where(child => child.Parent == item.Id));
}
});
I don't know if it's more cleaner but if you want a unique linq instruction you can try this :
var result = GetProductList().Where(p => p.Parent == null)
.SelectMany(p => list.Where(c => c.Parent == p.Id)
.Concat(new[] { p })
.OrderBy(c => c.Parent.HasValue)
.ThenBy(c => c.Id)
.ToList())
.ToList();
You should add a ParentId to the Product One and Product two, and the, will be easier to solve it.
If Parent One is 1, and Parent Two is 2, only do this
var result = parents.OrderBy(x => x.Parent).ThenBy(x => x.Id);
Maybe using linq this way:
var groupList = from c in products
where c.Parent.HasValue
group c by c.Parent into r
join p in products on r.Key equals p.Id
orderby p.Name
select new { Parent = p, Children = r };
It's very easy and complicated way, In 'res' variable you'll see this kind of situation - parent1 > child.1.1 > child.1.2 > parent2 > child.2.1 > child.2.2 > child.2.3 > parent3:
//items is a list of unsorted objects
var res = items.OrderBy(x =>
{
if (x.ParentId == null)
return x.Id;
else
return x.ParentId;
}).ThenBy(t => t.Id);

How to use local variables in a lambda expression

I have 2 list object of type of some class,
class person
{
public string id { get; set; }
public string name { get; set; }
}
List<person> pr = new List<person>();
pr.Add(new person { id = "2", name = "rezoan" });
pr.Add(new person { id = "5", name = "marman" });
pr.Add(new person { id = "3", name = "prithibi" });
List<person> tem = new List<person>();
tem.Add(new person { id = "1", name = "rezoan" });
tem.Add(new person { id = "2", name = "marman" });
tem.Add(new person { id = "1", name = "reja" });
tem.Add(new person { id = "3", name = "prithibi" });
tem.Add(new person { id = "3", name = "prithibi" });
Now i have to get all the ids from "pr" ListObject that has no entry or odd number of entries in the "tem" ListObejct. using lamda.
To do this i have used,
HashSet<string> inconsistantIDs = new HashSet<string>(pr.Select(p => p.id).Where(p => tem.FindAll(t => t.id == p).Count == 0 || tem.FindAll(t => t.id == p).Count % 2 != 0));
and it works fine.
but you can see from the code i have used tem.FindAll(t => t.id == p).Count twice to comapre with ==0 and %2!=0.
Is there any way to use tem.FindAll(t => t.id == p).Count once and
save it to a temporary variable and then compare this variable with
==0 and %2!=0.
More simply i just want to use it once for two condition here.
Use a statement lambda instead of an expression lambda
var inconsistantIDs = new HashSet<string>(
pr.Select(p => p.id).Where(p =>
{
var count = tem.FindAll(t => t.id == p).Count;
return count == 0 || count % 2 != 0;
}
));
Perhaps simply:
var query = pr.Where(p => { int c = tem.Count(p2 => p.id == p2.id); return c == 0 || c % 2 != 0; });
returns two persons:
2 "rezoan"
5 "marman"
Besides statement lambda you can use let clause:
HashSet<string> inconsistantIDs = new HashSet<string>(
from p in pr
let count = tem.FindAll(t => t.id == p).Count
where count == 0 || count % 2 != 0
select p.id
);
HashSet<string> inconsistantIDs = new HashSet<string>(
pr.Select(p => new { Id = p.id, Cnt = tem.FindAll(t => t.id == p.id).Count() })
.Where(p => p.Cnt == 0 || p.Cnt % 2 != 0)
.Select(p => p.Id);
On a side note, strictly performance wise, you would get better performance if you created a hash mapping of each ID to its count and then search it in a loop.
Right now you have a O(n*m) algorithm, which would be reduced to O(n+m):
// create a map (id -> count), O(m) operation
var dictionary = new Dictionary<string, int>();
foreach (var p in tem)
{
var counter = 0;
dictionary.TryGetValue(p.id, out counter);
counter++;
dictionary[p.id] = counter;
}
// search the map, O(n) operation
var results = new HashSet<string>();
foreach (var p in pr)
{
var counter = 0;
dictionary.TryGetValue(p.id, out counter);
if (counter == 0 || counter % 2 != 0)
results.Add(p.id);
}

Categories

Resources