如何用LINQ合并分层数据:将Location关联至对应City
解决方案
第一步:优化Location查找效率
不要每次用Where(x => x.ItemId == c.Id)做线性遍历,数据量大时效率极低。建议先把locations转成以ItemId为键的字典,后续查找时间复杂度为O(1):
var locationMap = locations.ToDictionary(loc => loc.ItemId);
第二步:合并数据到原有层级结构
无需破坏原有层级,直接遍历或用LINQ处理即可,提供两种实现方式:
方式1:直接修改原对象(简单直观)
你的record属性都设置了get; set;,可以直接修改原City的Location属性,完整保留原有层级:
foreach (var state in country.States) { foreach (var city in state.Cities) { locationMap.TryGetValue(city.Id, out var matchedLocation); city.Location = matchedLocation; } }
方式2:创建新的不可变结构(符合Record设计理念)
如果想保持record的不可变性(不修改原对象),用C# record的with表达式创建新对象树,原有对象不受影响:
var updatedCountry = country with { States = country.States.Select(state => state with { Cities = state.Cities.Select(city => city with { Location = locationMap.TryGetValue(city.Id, out var loc) ? loc : null }).ToList() }).ToList() };
完整示例代码
// 获取基础数据 var country = await _serviceRequestA.GetCountryData(); // 提取所有City的Id var cityIds = country.States.SelectMany(s => s.Cities).Select(c => c.Id).ToList(); // 获取Location列表 var locations = await _serviceRequestB.GetLocations(cityIds); // 构建Location映射字典 var locationMap = locations.ToDictionary(loc => loc.ItemId); // 方式1:修改原对象 foreach (var state in country.States) { foreach (var city in state.Cities) { locationMap.TryGetValue(city.Id, out var matchedLocation); city.Location = matchedLocation; } } // 或者方式2:创建新的不可变对象 // var updatedCountry = country with // { // States = country.States.Select(state => state with // { // Cities = state.Cities.Select(city => city with // { // Location = locationMap.TryGetValue(city.Id, out var loc) ? loc : null // }).ToList() // }).ToList() // }; // 返回处理后的对象 return country; // 或return updatedCountry;
说明
- 两种方式都完整保留Country→State→City的层级结构,无需拆分重组。
- 字典映射比反复线性查找效率提升显著,尤其在Location数量较多时。
- 若record属性为只读(仅
get;),只能用方式2;若属性可变,方式1更简洁。
内容的提问来源于stack exchange,提问作者MattoMK
相关产品推荐
相关产品推荐

