what is wrong with my GroupBy Query - c#

Hello all what is wrong with my GroupBy query ?
I have following class:
public class AssembledPartsDTO
{
public int PID { get; set; }
public McPosition Posiotion { get; set; }
public string Partnumber { get; set; }
public string ReelID { get; set; }
public int BlockId { get; set; }
public List<string> References { get; set; }
}
I am trying to perform following query:
assembledPcb.AssembledParts.GroupBy(entry => new
{
entry.PID,
entry.Posiotion.Station,
entry.Posiotion.Slot,
entry.Posiotion.Subslot,
entry.Partnumber,
entry.ReelID,
entry.BlockId
}).
Select( (key , val )=> new AssembledPartsDTO
{
BlockId = key.Key.BlockId,
PID = key.Key.PID,
Partnumber = key.Key.Partnumber,
ReelID = key.Key.ReelID,
Posiotion = new McPosition(key.Key.Station, key.Key.Slot, key.Key.Subslot),
References = val <-- ????
})
But the val that I have there is of type int and not the val of grouping that I can do there val.SelectMany(v => v).ToList(); any idea what is wrong in my code ?

The second parameter of Enumerable.Select is the index of the item in the sequence. So in this case it is the (zero based) number of the group. You just want to select the group, you don't need it's index:
var result = assembledPcb.AssembledParts.GroupBy(entry => new
{
entry.PID,
entry.Posiotion.Station,
entry.Posiotion.Slot,
entry.Posiotion.Subslot,
entry.Partnumber,
entry.ReelID,
entry.BlockId
})
.Select(g => new AssembledPartsDTO
{
BlockId = g.Key.BlockId,
PID = g.Key.PID,
Partnumber = g.Key.Partnumber,
ReelID = g.Key.ReelID,
Posiotion = new McPosition(g.Key.Station, g.Key.Slot, g.Key.Subslot),
References = g.SelectMany(entry => entry.References)
.Distinct()
.ToList()
});
(assuming that you want a list of distinct references)
Side-Note: you have a typo at the property-name: Posiotion

Related

Joining two lists of object optimization

I am looking for a way of optimizing my LINQ query.
Classes:
public class OffersObject
{
public List<SingleFlight> Flights { get; set; }
public List<Offer> Offers { get; set; } = new List<Offer>();
}
public class SingleFlight
{
public int Id { get; set; }
public string CarrierCode { get; set; }
public string FlightNumber { get; set; }
}
public class Offer
{
public int ProfileId { get; set; }
public List<ExtraOffer> ExtraOffers { get; set; } = new List<ExtraOffer>();
}
public class ExtraOffer
{
public List<int> Flights { get; set; }
public string Name { get; set; }
}
Sample object:
var sampleObject = new OffersObject
{
Flights = new List<SingleFlight>
{
new SingleFlight
{
Id = 1,
CarrierCode = "KL",
FlightNumber = "1"
},
new SingleFlight
{
Id = 2,
CarrierCode = "KL",
FlightNumber = "2"
}
},
Offers = new List<Offer>
{
new Offer
{
ProfileId = 41,
ExtraOffers = new List<ExtraOffer>
{
new ExtraOffer
{
Flights = new List<int>{1},
Name = "TEST"
},
new ExtraOffer
{
Flights = new List<int>{2},
Name = "TEST"
},
new ExtraOffer
{
Flights = new List<int>{1,2},
Name = "TEST"
}
}
}
}
};
Goal of LINQ query:
List of:
{ int ProfileId, string CommercialName, List<string> fullFlightNumbers }
FullFlightNumber should by created by "Id association" of a flight. It is created like: {CarrierCode} {FlightNumber}
What I have so far (works correctly, but not the fastest way I guess):
var result = sampleObject.Offers
.SelectMany(x => x.ExtraOffers,
(a, b) => {
return new
{
ProfileId = a.ProfileId,
Name = b.Name,
FullFlightNumbers = b.Flights.Select(f => $"{sampleObject.Flights.FirstOrDefault(fl => fl.Id == f).CarrierCode} {sampleObject.Flights.First(fl => fl.Id == f).FlightNumber}").ToList()
};
})
.ToList();
Final note
The part that looks wrong to me is:
.Select(f => $"{sampleObject.Flights.FirstOrDefault(fl => fl.Id == f)?.CarrierCode} {sampleObject.Flights.FirstOrDefault(fl => fl.Id == f)?.FlightNumber}").ToList()
I am basically looking for a way of "joining" those two lists of the OffersObject by Flight's Id.
Any tips appreciated.
If there will only be a few flights defined in sampleObject.Flights, a sequential search using a numeric key is hard to beat.
However, if the number of flights times the number of offers is substantial (1000s or more), I would suggest loading the list of flights into a dictionary with Id as the key for efficient lookup. Something like:
var flightLookup = sampleObject.Flights.ToDictionary(f => f.Id);
And then calculate your FullFlightNumbers as
FullFlightNumbers = b.Flights
.Select(flightId => {
flightLookup.TryGetValue(flightId, out SingleFlight flight);
return $"{flight?.CarrierCode} {flight?.FlightNumber}";
})
.ToList()
TryGetValue above will quietly return a null value for flight if no match is found. If you know that a match will always be present, the lookup cold alternately be coded as:
SingleFlight flight = flightLookup[flightId];
The above also uses a statement lambda. In short, lambda functions can have either expression or statement blocks as bodies. See the C# reference for more information.
I'd suggest replacing the double .FirstOrDefault() approach with .IntersectBy(). It is available in the System.Linq namespace, starting from .NET 6.
.IntersectBy() basically filters sampleObject.Flights by matching the flight ID for each flight in sampleObject with flight IDs in ExtraOffers.Flights.
In the code below, fl => fl.Id is the key selector for sampleObject.Flights (i.e. fl is a SingleFlight).
var result = sampleObject.Offers
.SelectMany(x => x.ExtraOffers,
(a, b) => {
return new
{
ProfileId = a.ProfileId,
Name = b.Name,
FullFlightNumbers = sampleObject.Flights
.IntersectBy(b.Flights, fl => fl.Id)
.Select(fl => fl.FullFlightNumber) // alternative 1
//.Select(fl => $"{fl.CarrierCode} {fl.FlightNumber}") // alternative 2
.ToList()
};
})
.ToList();
In my suggestion I have added the property FullFlightNumber to SingleFlight so that the Linq statement looks slightly cleaner:
public class SingleFlight
{
public int Id { get; set; }
public string CarrierCode { get; set; }
public string FlightNumber { get; set; }
public string FullFlightNumber => $"{CarrierCode} {FlightNumber}";
}
If defining SingleFlight.FullFlightNumber is not possible/desirable for you, the second alternative in the code suggestion can be used instead.
Example fiddle here.

Combine two different types into one linq query and sort it

I have two database tables and I'm attempting to create a union query from them. They have different structures:
public partial class Notes
{
public int ID { get; set; }
public int VisitID { get; set; }
public string Note { get; set; }
public DateTime PostDate { get; set; }
public decimal AcctBalance {get; set; }
}
public partial class SystemNotes
{
public int ID {get; set;}
public int VisitID {get; set;}
public int FacilityID {get; set;}
public string Note {get; set;
public DateTime NoteDate {get ;set; }
}
What I want to do is end up with a list of all the data in Notes format sorted by PostDate. What I've tried so far is this:
List<Notes> requests = new List<Notes>();
requests = _context.Notes.Where(i => i.VisitID == VisitID && i.isActive == true).ToList();
List<SystemNotes> requests_s = new List<SystemNotes>();
requests_s = _context.SystemNotes.Where(i => i.VisitID == VisitID).ToList();
var unionA = from a in requests
select new
{
a.ID,
a.VisitID,
a.Note,
a.PostDate,
a.AcctBalance
};
var unionB = from b in requests_s
select new Notes()
{
ID = b.ID,
VisitID = (int)b.VisitID,
Note = b.Note,
PostDate = b.NoteDate,
AcctBalance = (decimal)0.00
};
List<Object> allS = (from x in unionA select (Object)x).ToList();
allS.AddRange((from x in unionB select (Object)x).ToList());
However, PostDate is no longer recognized as an element inside the Object so I can't sort on it. Also, it's in Object format not in Notes format which is what I want for where I'm sending my data. I'm stuck on this one point. Can you assist? Or am I doing this the wrong way in general?
If I correctly understand what you want:
List<Notes> myNotes = new List<Notes> {
new Notes () {
ID = 1,
VisitID = 2
}
};
List<SystemNotes> mySystemNotes = new List<SystemNotes> {
new SystemNotes () {
ID = 3,
VisitID = 4
}
};
var result = myNotes.Select (mn => new { mn.ID, mn.VisitID })
.Union(mySystemNotes.Select (msn => new { msn.ID, msn.VisitID }))
.OrderByDescending(a=>a.ID);
foreach (var currentItem in result)
{
Console.WriteLine ("ID={0}; VisitID={1}", currentItem.ID, currentItem.VisitID);
}

Sorting and Updating a Generic List of Object based on a Sub Object

I have the following objects:
public class TestResult
{
public string SectionName { get; set; }
public int Score { get; set; }
public int MaxSectionScore { get; set; }
public bool IsPartialScore { get; set; }
public string Name { get; set; }
public int NumberOfAttempts { get; set; }
}
public class TestResultGroup
{
public TestResultGroup()
{
Results = new List<TestResult>();
Sections = new List<string>();
}
public List<TestResult> Results { get; set; }
public List<string> Sections { get; set; }
public string Name { get; set; }
public int Rank { get; set; }
}
So, a TestResultGroup can have any number of results of type TestResult. These test results only differ by their SectionName.
I have a List<TestResultGroup> which I need to sort into descending order based on a score in the Results property, but only when Results has an item whos SectionName = "MeanScore" (if it doesnt have this section we can assume a score of -1). How would I go about ordering the list? Ideally I would also like to apply the result of this ordering to the Rank property.
Many Thanks
List<TestResultGroup> groups = ...
// group test result groups by the same score and sort
var sameScoreGroups = groups.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(gr => gr.Key);
int rank = 1;
foreach (var sameScoreGroup in sameScoreGroups)
{
foreach (var group in sameScoreGroup)
{
group.Rank = rank;
}
rank++;
}
// to obtain sorted groups:
var sortedGroups = groups.OrderByDescending(gr => gr.Rank).ToArray();
Or even write one expression with a side effect:
List<TestResultGroup> groups = ...
int rank = 1;
var sortedGroups = groups
.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(grouping => grouping.Key)
.SelectMany(grouping =>
{
int groupRank = rank++;
foreach (var group in grouping)
{
group.Rank = groupRank;
}
return grouping;
})
.ToArray(); // or ToList

Need a alternate approch to do SQL to LINQ conversion

I'm trying to get a particular result set for my View to bind. I'm new to Linq expression, so I'm not very sure about the different ways of doing it.
Here is my MenuModel
public class MenuModel : DisposeBase
{
public string ParentID { get; set; }
public string ParentName { get; set; }
public List<MenuItemModel> MenuItems { get; set; }
}
My MenuItemModel
public class MenuItemModel : DisposeBase
{
public string ChildID { get; set; }
public string ChildName { get; set; }
public string PageURL { get; set; }
}
MenuModel is the output type I'm expecting as a result set. I'm getting result set of type DataTable from backend
DataTable dtable = oDatabase.ExecuteAdapter(System.Data.CommandType.StoredProcedure, "SP_GETUSERNAVMENUDATA");
Here is my SQL result set,
My DataTable will looks like this
Now I need to convert this Datatable to type MenuModel.
I tried to Query distinct MenuModel and based on that I'm building MenuItemModel object.
List<MenuModel> lstMenuModel = dtable.DataTableToList<MenuModel>()
.GroupBy(p => new { p.ParentID, p.ParentName })
.Select(g => g.First())
.ToList<MenuModel>();
foreach (MenuModel parentItem in lstMenuModel)
{
List<MenuItemModel> lstUserMenuItemData = dtable.DataTableToList<MenuItemModel>()
.Select(i => new { i.ChildID, i.ChildName, i.PageURL, i.ParentID })
.Where(i => i.ParentID.Equals(parentItem.ParentID))
.ToList<MenuItemModel>();
}
But still I'm getting conversion error while building MenuItemModel. Now I wanted to know, is there any best practice to do this same conversion of these nested class type? I'm sure there should be something simple to do so.
Any help could be appreciated. Thanks!
Note: DataTableToList is a method that will convert DataTable object to specific generic type
Its not clear what your DataTableToList<MenuModel>() method is doing or returning, but it would need to return a collection of a model that contains all 5 properties represented in the data table.
Assuming you have the following model
public class MenuSQLSet
{
public string ParentID { get; set; }
public string ParentName { get; set; }
public string ChildID { get; set; }
public string ChildName { get; set; }
public string PageURL { get; set; }
}
then your query should be
List<MenuModel> lstMenuModel = dtable.DataTableToList<MenuSQLSet>()
.GroupBy(x => new { x.ParentID, x.ParentName })
.Select(x => new MenuModel()
{
ParentID = x.Key.ParentID,
ParentName = x.Key.ParentName,
MenuItems = x.Select(y => new MenuItemModel()
{
ChildID = y.ChildID,
ChildName = y.ChildName,
PageURL = y.PageURL
}).ToList()
}).ToList();
Alternatively you can use .AsEnumerable() on the DataTable and reference the column names
List<MenuModel> lstMenuModel = dtable.AsEnumerable()
.GroupBy(x => new { ParentID = x["ParentID"], ParentName = x["ParentName"] })
.Select(x => new MenuModel()
{
ParentID = x.Key.ParentID,
ParentName = x.Key.ParentName,
MenuItems = x.Select(y => new MenuItemModel()
{
ChildID = y["ChildID"],
....

How to extract result of Linq Expression?

My result Expression is
var result = dtFields.AsEnumerable().Join(dtCDGroup.AsEnumerable(),
fieldList=>fieldList.Field<string>("CDGroupID"),
cd=>cd.Field<string>("CDGroupID"),
(fieldList,cd) => new
{
FieldID = fieldList.Field<string>("FieldID"),
Name = cd.Field<string>("Name"),
CDCaption = fieldList.Field<string>("CDCaption"),
Priority = ((cd.Field<string>("Priority") == null) ? 99 : cd.Field<int>("Priority")),
fldIndex = fieldList.Field<string>("fldIndex")
}).OrderBy(result => result.Priority).ThenBy(result => result.fldIndex);
Casting above result to array or list throws an invalid cast exception.
How can extract result of above expression?
Add .ToArray() or .ToList() call respectively
Try to add a strongly typed type:
public class NewModule
{
public int FieldID { get; set; }
public string Name { get; set; }
public string CDCaption { get; set; }
public int Priority { get; set; }
public int fldIndex { get; set; }
}
instead of the anonymous type then you could use ToList<NewModule>() like this:
var result = dtFields.AsEnumerable().Join(dtCDGroup.AsEnumerable(),
fieldList=>fieldList.Field<string>("CDGroupID"),
cd=>cd.Field<string>("CDGroupID"),
(fieldList,cd) => new NewModule
{
FieldID = fieldList.Field<string>("FieldID"),
Name = cd.Field<string>("Name"),
CDCaption = fieldList.Field<string>("CDCaption"),
Priority = ((cd.Field<string>("Priority") == null) ? 99 : cd.Field<int>("Priority")),
fldIndex = fieldList.Field<string>("fldIndex")
}).OrderBy(result => result.Priority)
.ThenBy(result => result.fldIndex)
.ToList<NewModule>();

Categories

Resources