C# 如何在比较两个列表的基础上将属性更新为true或false

C# 如何在比较两个列表的基础上将属性更新为true或false,c#,linq,C#,Linq,我有两张单子 class obj1 { public string country{ get; set; } public string region{ get; set; } } class obj2 { public string country{ get; set; } public string region { get; set; } public string XYZ { get; set; } public bool ToBeChanged{

我有两张单子

class obj1
{
  public string country{ get; set; }
  public string region{ get; set; }
}

class obj2
{
   public string country{ get; set; }
   public string region { get; set; }
   public string XYZ    { get; set; }
   public bool ToBeChanged{ get; set; }
}
  • 第一个列表如下所示:

    List<obj1> alist = new List<obj1>();
    alist.Add("US", "NC");
    alist.Add("US", "SC");
    alist.Add("US", "NY");
    
    List alist=newlist();
    添加(“美国”、“北卡罗来纳州”);
    添加(“美国”、“SC”);
    添加(“美国”、“纽约”);
    
  • 第二个列表(
    list alist2
    )可能包含1000个条目,其中包含许多国家和地区的组合

  • 如果第二个(alist2)列表属性(国家和地区)与第一个(alist1)匹配,则我需要将属性“更改为”True否则为false

    请帮忙

    谢谢,
    瓦伊巴夫

    评论中的两点和我的想法:

    • 有些人不确定你的匹配标准到底是什么。但在我看来,很明显,你在“国家”和“地区”上是匹配的。然而,在未来,请明确说明这一点
    • 有一条评论批评了您对变量名的选择。这种批评是完全有道理的。当您对代码所做的事情几乎没有任何提示时,代码更容易维护,而变量名对于这一点至关重要
    现在,关于我的具体解决方案:

    在下面的代码中,我重命名了一些对象,以明确它们的用途。我想重命名“obj2”,但我将把它留给你,因为我不确定你打算用它做什么,而且我肯定不知道“XYZ”是干什么的。下面是重命名的类,添加了一些构造函数以帮助列表构造

    class RegionInfo {
        public RegionInfo(string country, string region) { 
            this.country = country; 
            this.region = region; 
        }
        public string country{ get; set; }
        public string region{ get; set; }
    }
    
    class obj2 {
        public obj2 (string country, string region, string XYZ) {
            this.country = country;
            this.region = region;
            this.XYZ = XYZ;
        }
        public string country{ get; set; }
        public string region { get; set; }
        public string XYZ    { get; set; }
        public bool ToBeChanged{ get; set; }
    }
    
    我使用LINQ联接来匹配这两个列表,只输出联接的“obj2”端,然后循环结果以切换“ToBeChanged”值

    var regionInfos = new List<RegionInfo>() {
        new RegionInfo("US", "NC"),
        new RegionInfo("US", "SC"),
        new RegionInfo("US", "NY")
    };
        
    var obj2s = new List<obj2> {
        new obj2("US", "NC", "What am I for?"),
        new obj2("US", "SC", "Like, am I supposed to be the new value?"),
        new obj2("CA", "OT", "XYZ doesn't have a stated purpose")
    };
            
    var obj2sToChange = obj2s
        .Join(
            regionInfos, 
            o2 => new { o2.country, o2.region }, 
            reg => new { reg.country, reg.region },
            (o2,reg) => o2
        );
        
    foreach (var obj2 in obj2sToChange)
        obj2.ToBeChanged = true;
        
    obj2s.Dump(); // using Linqpad, but you do what works to display
        
    
    var regioninfo=新列表(){
    新区域信息(“美国”、“北卡罗来纳州”),
    新区域信息(“美国”、“SC”),
    新区域信息(“美国”、“纽约”)
    };
    var obj2s=新列表{
    新obj2(“美国”、“北卡罗来纳州”、“我是干什么的?”),
    新的obj2(“我们”、“SC”、“我应该是新值吗?”),
    新obj2(“CA”、“OT”、“XYZ无明确目的”)
    };
    var obj2sToChange=obj2s
    .加入(
    区域信息,
    o2=>新{o2.国家,o2.地区},
    reg=>新{reg.country,reg.region},
    (o2,reg)=>o2
    );
    foreach(obj2sToChange中的变量obj2)
    obj2.ToBeChanged=true;
    obj2s.Dump();//使用Linqpad,但您要做的就是显示
    
    这导致:

    国家 区域 XYZ 改变 美国 数控 我是干什么的? 真的 美国 联合国安全理事会 比如,我应该成为新的价值观吗? 真的 加利福尼亚州 OT XYZ没有明确的用途 假的
    首先,使用LINQ,您永远无法更改源代码。您只能从源中提取数据。之后,可以使用提取的数据更新源

    如果第二个(alist2)列表属性(国家和地区)与第一个(alist1)匹配,则需要将属性“ToBeChanged”更新为“True”,否则为false

    这不是一个适当的要求
    alist1
    是一系列
    obj1
    对象。我认为,如果alist1中的任何obj1项的[country,region]组合与相关obj2的[country,region]组合相匹配,则您希望某个obj2的属性被更改为true

    要求获取alist2中的所有obj2,这些obj2的[country,region]组合与alist1中obj1对象的任何[country,region]组合相匹配

    您可能考虑过使用
    Where
    进行此操作。类似于“其他列表中[国家、地区]组合的位置”。每当您需要查找一个项目是否在另一个列表中时,请考虑使用

    的重载之一。 问题是,每个obj2中的[Country,Region]组合都可以转换为obj1类的对象,但如果要检查它们是否相等,则需要按引用进行比较,而需要按值进行比较

    IEqualityComparer<obj1> comparer = Obj1Comparer.ByValue;
    
    IEnumerable<obj2> obj2sThatNeedToBeChanged = alist2.Select(obj2 => new
    {
        Obj1 = new Obj1
        {
            Country = obj2.Country,
            Region = obj2.Region,
        },
    
        Original = obj2,
    })
    .Where(item => alist.Contains(item.CountryRegionCombination, comparer))
    .Select(item => item.Original);
    
    有两种解决方案:

    • 创建按值比较obj1的EqualityComparer
    • 创建[国家,地区]作为匿名类型。匿名类型总是按值进行比较
    后者是最简单的,所以我们先做一个

    使用匿名类型进行比较 首先将列表转换为包含[国家、地区]组合的匿名类型:

    var eligibleCountryRegionCombinations = alist.Select(obj1 => new
    {
        Country = obj1.Country,
        Region = obj1.Region,
    });
    
    注意,最后我没有使用ToList:创建了可枚举项,但序列还没有被枚举。在LINQ术语中,这称为延迟执行或延迟执行

    IEnumerable<obj2> obj2sThatNeedToBeChanged = alist2.Select(obj2 => new
    {
        CountryRegionCombination = new
        {
            Country = obj2.Country,
            Region = obj2.Region,
        },
    
        Original = obj2,
    })
    .Where(item => eligibleCountryRegionCombinations.Contains(
                   item.CountryRegionCombination))
    .Select(item => item.Original);
    
    更改正在枚举的源可能很危险。在这种情况下,这不是问题,因为您更改的字段不用于创建枚举。不过,我认为,由于将来可能会发生变化,在开始更改源代码之前先做一个收费列表更安全

    创建一个相等比较器 Enumerable.Contains的重载之一具有参数比较器。这需要一个
    IEqualityComparer

    返回哪个HashCode取决于调用的频率,例如在字典、Contains等比较器中

    这些国家和地区有多“不同”?不同的国家可能也意味着不同的地区。因此,如果只计算国家的散列码,可能就足够有效了。如果一个国家有很多很多地区,那么如果一个地区只在一个国家(OberAmmerGau可能只在德国),或者只有几个地区(会有多少个“新阿姆斯特丹”地区),那么计算地区的哈希代码可能会更好,那么您根本不必检查该国家

    因为我们有一个相等比较器,所以不需要将alist转换为匿名类型,我们可以指定
    包含的
    应按值进行比较

    IEqualityComparer<obj1> comparer = Obj1Comparer.ByValue;
    
    IEnumerable<obj2> obj2sThatNeedToBeChanged = alist2.Select(obj2 => new
    {
        Obj1 = new Obj1
        {
            Country = obj2.Country,
            Region = obj2.Region,
        },
    
        Original = obj2,
    })
    .Where(item => alist.Contains(item.CountryRegionCombination, comparer))
    .Select(item => item.Original);
    
    IEqualityComparer comparer=Obj1Comparer.ByValue;
    需要更改的IEnumerable OBJ2ST=alist2。选择(obj2=>new
    {
    Obj1=新Obj1
    {
    国家=对象J2.国家,
    区域=对象J2。区域,
    },
    
    public override int GetHashCode (obj1 x)
    {
        if (x == null) return 87966354;  // just a number
    
        return CountryComparer.GetHashCode(x.Country)
            ^ RegionComparer.GetHashCode(x.Region);
    }
    
    IEqualityComparer<obj1> comparer = Obj1Comparer.ByValue;
    
    IEnumerable<obj2> obj2sThatNeedToBeChanged = alist2.Select(obj2 => new
    {
        Obj1 = new Obj1
        {
            Country = obj2.Country,
            Region = obj2.Region,
        },
    
        Original = obj2,
    })
    .Where(item => alist.Contains(item.CountryRegionCombination, comparer))
    .Select(item => item.Original);
    
    private static IEqualityComparer<string> CountryComparer => StringComparer.OrdinalIgnoreCase;
    private static IEqualityComparer<string> RegionComparer => StringComparer.OrdinalIgnoreCase;
    
    public static IEnumerable<Obj2> WhereSameLocation(
        this IEnumerable<Obj2> source,
        IEnumerable<Obj1> obj1Items)
    {
        // TODO: what to do if source == null?
    
        foreach (Obj2 obj2 in source)
        {
            // check if there is any obj1 with same [Country, Region]
            if (obj1Items
            .Where(obj1 => CountryComparer.Equals(obj2.Country, obj1.Country) 
                        && RegionComparer.Equals(obj2.Region, obj1.Region))
            .Any())
            {
                yield return obj2;
            }
        }
    }
    
    IEnumerable<Obj1> alist = ...
    IEnumerable<Obj2> alist2 = ...
           
    IEnumerable<obj2> obj2sThatNeedToBeChanged = alist2.WhereSameLocation(alist);