如何使两个列表具有相同的项目(但他们自己的序列)

时间:2018-03-18 02:35:04

标签: c# list

我有两个列表对象A和B,如下所示:

A=[
  {item:1, value:"ex1"},
  {item:2, value:"ex1"},
  {item:3, value:"ex1"},
  {item:4, value:"ex1"}]

B=[{item:1, value:"ex2"},
   {item:4, value:"ex2"},
   {item:2, value:"ex3"},
   {item:5, value:"ex3"}]

如何使B具有与A相同的项目/值,并且仍然保留其已有项目的序列?

我希望B删除第5项,因为A没有,并将第3项添加到最后。

我不想克隆A,我想修改B变成A。

我想要的是:

  • 当A没有物品时,删除B中的物品:item5

  • 当A和B都拥有它们时更新B中的项目:item1,item2,item4

  • 当A有它们时,将不存在的项目添加到B的结尾:item3

所以,结果应该是这样的:

B = [ {item:1, value:"ex1"},
      {item:4, value:"ex1"},
      {item:2, value:"ex1"},
      {item:3, value:"ex1"} ]

Mycode :(这就是我现在拥有的)

foreach (myClass itm in A)
{
     foreach (myClass fd in B)
     {
          if (itm.item == fd.item)
          {
              fd.value = itm.value;
          }
     }
 }

4 个答案:

答案 0 :(得分:1)

您可以编写一个扩展方法,通过迭代键并检查是否存在来合并列表。

static class ExtensionMethods
{
    static public void MergeInto<TKey,TValue>(this Dictionary<TKey,TValue> rhs, Dictionary<TKey,TValue> lhs)
    {
        foreach (var key in rhs.Keys.Union(lhs.Keys).Distinct().ToList())
        {
            if (!rhs.ContainsKey(key))
            {
                lhs.Remove(key);
                continue;
            }
            if (!lhs.ContainsKey(key))
            {
                lhs.Add(key, rhs[key]);
                continue;
            }
            lhs[key] = rhs[key];
        }
    }
}

测试程序:

public class Program
{
    public static Dictionary<int,string> A = new Dictionary<int,string>
    {
        { 1,"ex1" },
        { 2,"EX2" },
        { 3,"ex3" },
    };

    public static Dictionary<int,string> B = new Dictionary<int,string>
    {
        { 1,"ex1" },
        { 2,"ex2" },
        { 4,"ex4" }
    };

    public static void Main()
    {
        A.MergeInto(B);

        foreach (var entry in B )
        {
            Console.WriteLine("{0}={1}", entry.Key, entry.Value);
        }
    }
}

输出:

1=ex1
2=EX2
3=ex3

Code on DotNetFiddle

答案 1 :(得分:1)

不保留订单

如果您只想保留B的实例,但要使其所有元素都匹配A,那么您可以这样做:

B.Clear();
B.AddRange(A);

保留订单

如果您想保留订单,仍然可以使用上面的解决方案,但是您需要对传递给AddRange()的列表进行排序。这只是一点点工作。

首先,创建一个查找表,告诉您Item项最初出现的顺序。通用c#Dictionary使用哈希表作为键,因此最终会比扫描更有效反复列表。请注意,我们将B.Count传递给构造函数,以便它只需要一次分配空间,而不是在增长时重复分配。

var orderBy = new Dictionary<int,int>(B.Count);
for (int i=0; i<B.Count; i++) orderBy.Add(B[i].Item, i);

现在我们使用我们的解决方案,对输入列表进行排序:

B.Clear();
B.AddRange
(
    A.OrderBy( item => orderBy.GetValueOrFallback(item.Item, int.MaxValue) )
);

GetValueOrFallbackDictionary<,>上的一种简单扩展方法,可以更轻松地处理可能存在或不存在的密钥。传入所需的密钥,如果找不到密钥则返回一个值。在我们的例子中,我们传递int.MaxValue,以便将新项目附加到最后。

static public TValue GetValueOrFallback<TKey,TValue>(this Dictionary<TKey,TValue> This, TKey keyToFind, TValue fallbackValue)
{
    TValue result;
    return This.TryGetValue(keyToFind, out result) ? result : fallbackValue;
}

实施例

将所有这些与测试程序结合在一起:

public class MyClass
{
    public int Item { get; set; }
    public string Value { get; set; }
    public override string ToString() { return Item.ToString() + "," + Value; }
}

static public class ExtensionMethods
{
    static public TValue ValueOrFallback<TKey,TValue>(this Dictionary<TKey,TValue> This, TKey keyToFind, TValue fallbackValue)
    {
        TValue result;
        return This.TryGetValue(keyToFind, out result) ? result : fallbackValue;
    }

    static public void MergeInto(this List<MyClass> mergeFrom, List<MyClass> mergeInto)
    {
        var orderBy = new Dictionary<int,int>(mergeFrom.Count);
        for (int i=0; i<mergeInto.Count; i++) orderBy.Add(mergeInto[i].Item, i);
        mergeInto.Clear();
        mergeInto.AddRange
        (
            mergeFrom.OrderBy( item => orderBy.ValueOrFallback(item.Item, int.MaxValue) )
        );
    }
}

public class Program
{
    public static List<MyClass> A = new List<MyClass>
    {
            new MyClass { Item = 2,Value = "EX2" },
            new MyClass { Item = 3,Value = "ex3" },
            new MyClass { Item = 1,Value = "ex1" }
    };
    public static List<MyClass> B = new List<MyClass>
    {
            new MyClass { Item = 1,Value = "ex1" },
            new MyClass { Item = 2,Value = "ex2" },
            new MyClass { Item = 4,Value = "ex3" },
    };

    public static void Main()
    {
        A.MergeInto(B);
        foreach (var b in B) Console.WriteLine(b);
    }
}

输出:

1,ex1
2,EX2
3,ex3

Code on DotNetFiddle

答案 2 :(得分:0)

这是您在问题中指定的内容。我测试了它并且它有效:

class myClass
{
    public int item;
    public string value;
    //ctor:
    public myClass(int item, string value) { this.item = item; this.value = value; }
}

static void updateList()
{
    var listA = new List<myClass> { new myClass(1, "A1"), new myClass(2, "A2"), new myClass(3, "A3"), new myClass(4, "A4") };
    var listB = new List<myClass> { new myClass(1, "B1"), new myClass(4, "B4"), new myClass(2, "B2"), new myClass(5, "B5") };

    for (int i = 0; i < listB.Count; i++) //use index to be able to use RemoveAt which is faster
    {
        var b = listB[i];
        var j = listA.FindIndex(x => x.item == b.item);

        if (j >= 0) //A has this item, update its value
        {
            var v = listA[j].value;
            if (b.value != v) b.value = v;
        }
        else //A does not have this item
        {
            listB.RemoveAt(i);
        }
    }

    foreach (var a in listA)
    {
        //if (!listB.Contains(a)) listB.Add(a);
        if (!listB.Any(b => b.item == a.item)) listB.Add(a);
    }

}

答案 3 :(得分:-1)

你可以做类似的事情:

        var listA = new List<int> { 1, 3, 5 };
        var listB = new List<int> { 1, 4, 3 };

        //Removes items in B that aren't in A.
        //This will remove 4, leaving the sequence of B as 1,3
        listB.RemoveAll(x => !listA.Contains(x));

        //Gets items in A that aren't in B
        //This will return the number 5
        var items = listA.Where(y => !listB.Any(x => x == y));

        //Add the items in A that aren't in B to the end of the list
        //This adds 5 to the end of the list
        foreach(var item in items)
        {
            listB.Add(item);
        }

        //List B should be 1,3,5
        Console.WriteLine(listB);