Lets say I have two lists objects with this classes
class Sku
{
public string skuId { get; set; }
public int qty { get; set; }
public string city { get; set; }
}
class SkuWithCity
{
public string skuId { get; set; }
public string city { get; set; }
}
And I have two lists with objects:
List<Sku> skuList = new List<Sku>();
List<SkuWithCity> skuListWithCity = List<SkuWithCity>();
Imagine that in the first list(skuList), the property "City" of each object is null.
What I want to do is, using linq, select the sku objects that have the same skuId and add the city. Somethis like:
var result = from skuElements in skuList
from skuWCity in skuListWithCity
where skuElements.sku == skuWCity.sku
select skuElements
{
skuElements.city = skuWCity.city,
};
And get the whole object, not just the city
int order to get:
|Object | Qty | City
|---- |----|
|AAA | 2 | Panama|
|BBB | 5 | Rio De Janeiro|
is this even possible, get the whole object and modify one or many properties?
UPDATE: In real life the object that I'm trying to copy has a lot of members, that is why I'm trying to "copy" de object of list A and just modify some attributes using the match object of list B.
If you just want to update the existing objects instead of projecting to a new set of objects then first use a join to get the matching items.
var result = from skuElement in skuList
join skuWCity in skuListWithCity
on skuElements.skuId == skuWCity.skuId
select skuElements
{
skuElement,
skuWCity
};
Then iterate them and do the update.
foreach(var x in result)
{
x.skuElement.City = x.skuWCity.City;
}
Note this does not handle the case where more than one item in either list has more than one match in the other. I'm assuming that the lists have a one-to-one match already.
Alternatively you could just use a dictionary.
var cities = skuListWithCity.ToDictionary(s => s.skuId, s => s.City);
foreach(var s in skuList)
{
s.City = cities[s.skuId];
}
Note that this fails if there are duplicate skuId in skuListWithCity or if a skuId in skuList is not in skuListWithCity
You could use a join and then make a projection of the result set as below:
var result = from skuElements in skuList
join skuWCity in skuListWithCity
on skuElements.skuId equals skuWCity.skuId
select new Sku
{
skuId = skuElements.skuId,
qty = skuElements.qty,
city = skuWCity.city,
};
Related
Being rather new to LINQ, I am probably not looking for the correct search terms, that's why I am asking here.
I have a data transfer object (DTO) for the current timestep and a DTO for the previous timestep. I am intending to JOIN this information into a single (nested) list and display procentual changes (for all entries of the list). The nested contains nodes of type myObject. In other words, the list consists of a couple of parent entries and (different levels of) child elements of the same type. The merging step towards dataFullDto works perfectly for the parent entries only, for the child entries, the destination object does not contain the information of the previous timestep, PreviousValue and DeltaPercentage are null. Is it possible to perform that action on all nodes of a nested list without a foreach loop?
Assume that there is a function inputData(int timestep) which gives back all the required data. I inherited the following code which contains a LINQ inner JOIN:
var dataCurrentDto = inputData(time).GroupBy(x => x.Position) // Position just contains .Id and .Name
.Select(x => new myObject {
PositionId = x.Key?.Id,
PositionName = x.Key?.Name,
children = x.Select(Mapper.Map<myObjectDto>),
Value = x.Sum(y => y.Value),
PreviousValue = 0, // just to initialize, to be overwritten asap
DeltaPercentage = 0, // dito
});
var dataPreviousDto = inputData.Select(time-1).GroupBy(x => x.Position)
.Select(x => new myObject {
PositionId = x.Key?.Id,
PositionName = x.Key?.Name,
children = x.Select(Mapper.Map<myObjectDto>),
Value = x.Sum(y => y.Value),
});
dataFullDto = from current in dataCurrentDto join previous in dataPrevious on
new { current.PositionId, current.SubPositionId }
equals new { previous.PositionId, previous.SubPositionId }
select new myObjectDto {
PositionId = current.PositionId,
PositionName = current.PositionName,
children = current.children,
SubPositionId = current.SubPositionId,
SubPositionName = current.SubPositionName,
Value = current.Value,
PreviousValueAdjusted = previous.Value,
DeltaPercentage = (previous.!= null || previous.Value != 0) ?
(current.Value - previous.Value) / previous.Value : null,
};
I assume the GroupBy() was added to enable the sum over the children. But apparently, the sum over a leave node does not simply give back the value of the property itself?
The DTO is defined as follows:
namespace API.Models
{
public class myObjectDto
{
public int? PositionId { get; set; }
public string PositionName { get; set; }
public int SubPositionId { get; set; }
public string SubPositionName { get; set; }
public decimal? Value { get; set; }
public decimal? PreviousValue { get; set; }
public decimal? DeltaPercentage { get; set; }
}
}
The parent entries have SubPositionId = null and SubPositionName = null, the child entries have those two fields filled. Leaf-nodes have children=null.
References
LINQ: How to join nested Lists in an ObjectCollection
LINQ inner join for nested list
LINQ JOIN Child Collection
I am accustomed to using SQL left joins to get a list of all available options and determine what items are currently selected. My table structure would look like this
Table MainRecord -- recordId | mainRecordInfo
Table SelectedOptions -- recordId | optionId
Table AvailableOptions -- optionId | optionValue
and my query would look like this
SELECT optionValue, IIF(optionId IS NULL, 0, 1) AS selected
FROM AvailableOptions AS a
LEFT JOIN SelectedOptions AS s ON s.optionId = a.optionId AND a.recordId = 'TheCurrentRecord'
I am trying to replace this with Entity Framework, so I need help with both a model and a query -- they both need corrected.
public class MainRecord
{
public int recordId { get; set; }
public string mainRecordInfo { get; set; }
[ForeignKey("recordId")]
public List<SelectedOptions> selectedOptions { get; set; }
}
public class SelectedOptions
{
public int recordId { get; set; }
public int optionId { get; set; }
}
public class AvailableOptions
{
public int optionId { get; set; }
public string optionValue { get; set; }
}
Query
IQueryable<AvailableOptions> options = from o in context.AvailableOptions select o;
I can get a list of AvailableOptions, but how can I get a list and know which ones are selected?
If the number of selections and available options is small enough, you can do this in memory:
var selected = options.Join(record.selectedOptions, ao => ao.optionId, so => so.optionId, (a, s) => new { Available = a, Selected = s });
selected will now be a list of objects with Available and Selected as properties and will only contain those that matched in optionId value.
If you only wish to get a pure list of AvailableOptions that match, simply chain a Select to the join:
var selected = options.Join(record.selectedOptions, ao => ao.optionId, so => so.optionId, (a, s) => new { Available = a, Selected = s })
.Select(o => o.Available);
Not a complete answer, but it is really good to understand the navigational properties that you get from the model. Here is a query that most likely isn't exactly what you want but that demonstrate it
from ao in _db.AvailableOptions
where ao.recordId == "TheCurrentRecord" && ao.SelectedOptions.OptionId == 1
select new
MyPoxo {ao.SelectedOptions.Value ?? 0};
so instead of just having o you navigate through the joins that gets specified by the FKs. In this example I would assum AvailableOptions would have a linke to SelectedOptions.
I have two lists of Client Object:
public class Client
{
public int ClientID { get; set; }
public string Name { get; set; }
public string DCCode { get; set; }
public string CountryName { get; set; }
}
my list A hold 4 items ClientID , and name fields are populated,
My List B Hold the same 4 items but there is no name and hold the ClientID DCCode, and CountryName
i need to either Update list A DCCode and Countryname with corresponding values in list B
or create List C which it hold 4 items with complete value of list A and B together
Like :
List C L: item 1 : ClientID : 1, Name: XXYY, DCCode :4, CountryName: UK
I can do this using for loop, but i have been trying to use LINQ
i have tried the following codes but i could not find the correct way to get the result i want
Solution 1
Clients1.Where(i => Clients2.Any(a=> i.CLinetID == a.CLinetID))
Solution 2:
Clients1.Concat(Clients1).Concat(Clients2).ToList();
Any help would be welcomed
As you have the ClientID field populated in both lists join them by that property and project a new object populated with all fields:
var result = from c1 in Clients1
join c2 in Clients2 on c1.ClientID equals c2.ClientID
select new Client { ClientID = c1.ClientID, Name = c1.Name, DCCode = c2.DCCode, CountryName = c2.CountryName };
This will create the third list. You can also update the items of Clients1 likewise:
foreach (var c1 in Clients1)
{
var c2 = Clients2.FirstOrDefault(i => i.ClientID == c1.ClientID);
if(c2 != null)
{
c1.DCCode = c2.DCCode;
c1.CountryName = c2.CountryName;
}
}
For updating entities in first list you can create dictionary from second list - that will allow you to quickly find corresponding entity with O(1):
var clientsByID = listB.ToDictionary(c => c.ClientID);
foreach(var clientA in listA)
{
var clientB = clientsByID[clientA.ClientID];
clientA.DCCode = clientB.DCCode;
clientA.CountryName = clientB.CountryName;
}
You can also join two lists on ClientID property and produce new list from results (if enumerable is good for you, then I would go with query syntax instead):
var listC = listA.Join(listB,
a => a.ClientID,
b => b.ClientID,
(a,b) => new Client {
ClientID = a.ClientID,
Name = a.Name,
DCCode = b.DCCode,
CountryName = b.CountryName
}).ToList();
I have List of Custom type and I want to map this custom type to another complex type to represent one to many relationship.
ViewModel class contains data like this:
id name address
1 a add1
1 b add2
1 c add3
2 aa aaAdd
2 cc ccAdd
3 aaa aaaAdd
My code:
public class A //class to map
{
public int? Id { get; set; }
public List<B> Data { get; set; }
}
public class B
{
public string Name { get; set; }
public string Address { get; set; }
}
var data= context.Database.SqlQuery<ViewModel>(query).ToList();
// query to get data, I want to map this ViewModel to List<A> class.
I can do it by foreach but I want another solution.
Can I get the result of class A like this?
[
{id='1',[{a,add1},{b,add2},{c,add3}]},
{id='2',[{aa,aadd1},{cc,ccdd2}]},
{id='3',[{aaa,aaadd}]}
]
You can use GroupBy then construct the list of B using Select.
var data = context.Database.SqlQuery<ViewModel>(query)
.ToList() // Execute the query first
.GroupBy(q => q.Id)
.Select(g => new A
{
Id = g.Key,
Data = g.Select(b => new B { Name = b.Name, Address = b.Address }).ToList()
})
.ToList();
I have the following class
public class Booking
{
public string Group { get; set; }
public BookingType Type { get; set; }
public BookingStatus Status { get; set; }
public InvoiceFreq { get; set; }
public InvoiceLevel { get; set; }
}
That I'd need to group them by all properties in Linq so that the last two properties form one new field in the grouped result.
Like this:
Group Type Status InvoiceType
-------------------------------------------
Group1 Online New Daily-Single
Group2 Phone Accepted Daily-Single
Group3 Store Accepted Weekly-Single
Group3 Store Accepted Weekly-Bulk
Here the InvoiceFreq will be Daily / Weekly and the InvoiceLevel = Single or Bulk.
The query I have so far is this:
var query = from b in Bookings
group b by new
{
b.Group,
b.Type,
b.Status,
b.InvoicingFrequency,
b.InvoicingLevel
} into bookingGroup
select new BookingSummaryLine()
{
Group = bookingGroup.Key.UserGroup,
Type = bookingGroup.Key.UserGroup,
Status = bookingGroup.Key.FinancialStatus,
InvType = String.Format({0}-{1},bookingGroup.Key.InvoicingFrequency, bookingGroup.Key.InvoicingLevel
};
This of course does not give the desired result as the two properties are in the anonymous type separately.
Is there any way to achieve this in Linq?
This should work:
var query = from b in Bookings
group b by new
{
b.Group,
b.Type,
b.Status,
InvType = String.Format({0}-{1},b.InvoicingFrequency, b.InvoicingLevel)
} into bookingGroup
select new BookingSummaryLine()
{
Group = bookingGroup.Key.UserGroup,
Type = bookingGroup.Key.UserGroup,
Status = bookingGroup.Key.FinancialStatus,
InvType = bookingGroup.Key.InvType
};
I'm not as sure about the syntax for linq but for lambda I would use this. Basically use distinct if your trying to group on all columns like that.
Context.Booking.Distinct().Select(z=> new BookingSummaryLine { Group = z.Group, Type = z.Type, Status = z.Status, InvType = (z.InvoiceFrequency + "-" + z.InvoiceLevel });