C # сравнение двух больших списков элементов по определенному свойству - PullRequest
2 голосов
/ 13 марта 2019

У меня есть два больших списка элементов, класс которых выглядит следующим образом (оба списка одного типа):

public class Items
{
 public string ItemID { get; set; }
 public int QuantitySold { get; set; }
}


var oldList = new List<Items>(); // oldList

var newList = new List<Items>(); // new list

Старый список содержит элементы из базы данных, а новый список представляет элементы, извлеченные из API;

Оба списка могут быть очень большими с 10000+ элементами в каждом (всего 20000)

Мне нужно сравнить элементы из newList с элементами из "oldList" и посмотреть, какие элементы имеют одинаковый itemIDзначения имеют различное значение «Количество проданных», а те, которые имеют разное значение «Количество проданных», должны храниться в третьем списке, называемом «разные количественные показатели».

Я мог бы просто сделать двойной список foreach и сравнить значения, но так какоба списка велики, производительность с двойным циклом foreach ужасна, и я не могу этого сделать ...

Может кто-нибудь помочь мне с этим?

@ YamamotoTetsua Я ужеиспользуя IEqualityComparer для получения желаемого результата, однако он не дает ожидаемых результатов.Вот почему ... У меня есть первый IEqualityComparer, который выглядит следующим образом:

 public class MissingItemComparer : IEqualityComparer<SearchedUserItems>
    {
        public static readonly IEqualityComparer<SearchedUserItems> Instance = new MissingItemComparer();

        public bool Equals(SearchedUserItems x, SearchedUserItems y)
        {
            return x.ItemID == y.ItemID;
        }

        public int GetHashCode(SearchedUserItems x)
        {
            return x.ItemID.GetHashCode();
        }
    }

Использование этого IEqualityComparer в основном дает мне элементы из newList, которых нет в моей базе данных, например:

var missingItems= newItems.Except(competitor.SearchedUserItems.ToList(), MissingItemComparer.Instance).ToList();

Теперь в этом списке у меня будет список элементов, которые являются новыми из API и которых нет в моей БД ...

Второй IEqualityComparer основан на различном КоличествоПродано из старого и нового списка:

public class ItemsComparer : IEqualityComparer<SearchedUserItems>
    {
        public static readonly IEqualityComparer<SearchedUserItems> Instance = new ItemsComparer();
        public bool Equals(SearchedUserItems x, SearchedUserItems y)
        {
            return (x.QuantitySold == y.QuantitySold);
        }
        public int GetHashCode(SearchedUserItems x)
        {
            return x.ItemID.GetHashCode();
        }
    }

Пример использования:

var differentQuantityItems = newItems.Except(competitor.SearchedUserItems.ToList(), ItemsComparer.Instance).ToList();

Проблема с этими двумя сравнителями равенства заключается в том, что первый из них, например, возвратит отсутствующие itemID:

123124124

123124421

512095902

И они действительно отсутствуют в моем старом списке ... Однако второй IEQualityComparer также будет возвращать эти элементы как различные элементы количества, они действительно есть, но их нет в старом списке. Поэтому их не следует включать ввторой список.

Ответы [ 4 ]

6 голосов
/ 13 марта 2019

Это идеальный кандидат для LINQ Регистрация :

var differentQuantityItems =
    (from newItem in newList
     join oldItem in oldList on newItem.ItemID equals oldItem.ItemID
     where newItem.QuantitySold != oldItem.QuantitySold
     select newItem).ToList();

Это вернет все новые предметы, которые имеют соответствующий старый товар с различным количеством проданных. Если вы хотите также включить новые элементы без соответствующего старого элемента, используйте left external join :

var differentQuantityItems =
    (from newItem in newList
     join oldItem in oldList on newItem.ItemID equals oldItem.ItemID into oldItems
     from oldItem in oldItems.DefaultIfEmpty()
     where oldItem == null || newItem.QuantitySold != oldItem.QuantitySold
     select newItem).ToList();

В обоих случаях оператор соединения используется для быстрой корреляции элементов с одинаковым ItemID. Затем вы можете сравнить количество проданных или любые другие свойства.

1 голос
/ 13 марта 2019

Этот код будет выполняться менее чем за секунду, даже если совпадений не будет вообще (также менее чем за секунду, если все совпадение).

Он вернет все элементы, которые существуют в обоих списках (то есть тот же ItemID), но с другим QuantitySold.

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp5
{
    class Program
    {
        public class Items
        {
            public string ItemID { get; set; }
            public int QuantitySold { get; set; }
        }

        static void Main(string[] args)
        {
            // Sample data
            var oldList = new List<Items>();
            oldList.AddRange(Enumerable.Range(0, 20000).Select(z => new Items() { ItemID = z.ToString(), QuantitySold = 4 }));

            var newList = new List<Items>();
            newList.AddRange(Enumerable.Range(0, 20000).Select(z => new Items() { ItemID = z.ToString(), QuantitySold = 5 }));

            var results = oldList.Join(newList,
                                            left => left.ItemID,
                                            right => right.ItemID,
                                            (left, right) => new { left, right })
                                .Where(z => z.left.QuantitySold != z.right.QuantitySold).Select(z => z.left);

            Console.WriteLine(results.Count());
            Console.ReadLine();
        }
    }
}

Использование z.left означает, что будет возвращен только один элементов - если вы хотите и старое, и новое, вместо этого используйте:

var results = oldList.Join(newList,
                                left => left.ItemID,
                                right => right.ItemID,
                                (left, right) => new { left, right })
                    .Where(z => z.left.QuantitySold != z.right.QuantitySold)
                    .Select(z => new[] { z.left, z.right })
                    .SelectMany(z => z);
1 голос
/ 13 марта 2019

С точки зрения сложности большого O, просто сравнение списков во вложенном цикле for будет в классе O (n * m) , то есть n размер списка в БД и m размер списка, извлеченного из API.

Чтобы повысить производительность, вы можете отсортировать два списка, которые будут стоить O (n log (n) + m log (m)) , а затем вы можете найти новые предметы в O (n + m) . Следовательно, общая сложность вашего алгоритма будет тогда в классе O (n log (n) + m log (m)) .

Вот идея времени, которое потребовалось бы, сравнивая квадратичное решение с суперлинейным.

0 голосов
/ 13 марта 2019

Вы можете подумать об использовании предложения Except с пользовательским написанием IEqualityComparer что-то вроде ниже

var oldList = new List<Item>(); // oldList
var newList = new List<Item>(); // new list
var distinctList = newList.Except(oldList,new ItemEqualityComparer()).ToList();



class ItemEqualityComparer : IEqualityComparer<Item>
        {
            public bool Equals(Item i1, Item i2)
            {
                if (i1.ItemID == i2.ItemID && i1.QuantitySold != i2.QuantitySold)
                    return false;
                return true;
            }

            public int GetHashCode(Item item)
            {
                return item.ItemID.GetHashCode();
            }
        }

public class Item
        {
            public string ItemID { get; set; }
            public int QuantitySold { get; set; }
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...