#c_sharp #list
Есть два файла. Считываю из них оба значения в списки. Хочу взять уникальные значения из обоих списков, однако процесс поиска и сравнения идет очееень долго(строк в обоих списках ~300к). Подскажите как ускорить процесс поиска и сравнения*? var lst1 = File.ReadAllLines(@ "D:\test\1.csv").ToList(); var lst2 = File.ReadAllLines(@ "D:\\test\2.csv").ToList(); var rez = lst2.Where(x => !MySequenceContains(lst1, x)). Select(q => string.Join(";", q)).ToList(); } private bool MySequenceContains(List < string > x, string y) { bool contains = false; index++; label2.Text = index.ToString(); foreach(var a in x) { // ToDo: tweak the string comparison as needed if (string.Compare(a.Split(';')[0], y.Split(';')[0], StringComparison.InvariantCultureIgnoreCase) == 0 && string.Compare(a.Split(';')[14], y.Split(';')[14], StringComparison.InvariantCultureIgnoreCase) == 0) { contains = true; break; } } return contains; }
Ответы
Ответ 1
Во-первых, ваш код возвращает строки, которые есть во втором файле, но нет в первом. Он не возвращает строки, которые есть в первом, но нет во втором. Во-вторых, вы используете Split внутри цикла, т.е. одни и те же строки сплитите много раз. В-третьих, вы внутри цикла по одному файлу делаете цикл по другому (ищете это значение в другом файле. Если объемы файлов одинаковые, то алгоритм медленный N^2. Если предварительно выполнить Split для всех входных строк и отсортировать списки по вашему критерию, то отобрать неповторяющиеся элементы можно в одном цикле сразу по двум (отсортированным) спискам. Сложность получается 2N + 2NlogN(сортировка). Вот пример, извиняюсь, что нелаконично: private Listmy(List x, List y) { var rez = new List (); var lst1 = new List (x.Select(s => s.Split(';'))); var lst2 = new List (y.Select(s => s.Split(';'))); lst1.Sort(MyComarer.Comparer); lst2.Sort(MyComarer.Comparer); var isPresentInX = false; int j = 0; for (int i = 0; i < lst1.Count; i++) { if (j >= lst2.Count) { rez.Add(string.Join(";", lst1[i])); continue; } var comp = MyComarer.Comparer.Compare(lst1[i], lst2[j]); while (comp > 0 && j < lst2.Count) { if (!isPresentInX) rez.Add(string.Join(";", lst2[j])); j++; if (j < lst2.Count) { if (MyComarer.Comparer.Compare(lst2[j], lst2[j - 1]) != 0) isPresentInX = false; comp = MyComarer.Comparer.Compare(lst1[i], lst2[j]); } } if (comp != 0) rez.Add(string.Join(";", lst1[i])); else isPresentInX = true; } return rez; } private class MyComarer : IComparer { public static MyComarer Comparer { get; } = new MyComarer(); public int Compare(string[] x, string[] y) { var res = string.Compare(x[0], y[0], StringComparison.InvariantCultureIgnoreCase); if (res == 0) res = string.Compare(x[14], y[14], StringComparison.InvariantCultureIgnoreCase); return res; } } Ответ 2
lst1.Concat(lst2).ToList() .GroupBy(x=>x.Split(';')[0]+x.Split(';')[14]) .Select(g=>g.First())Ответ 3
Альтернативный вариант: использовать HashSetвместе с его методом ExceptWith При создании объекта HashSet ему можно передать IEqualityComparer , который будет использоваться для сравнения элементов. Он может выглядеть так: class MySequenceEqualityComparer : IEqualityComparer { public bool Equals(string x, string y) { var xSplit = x.Split(';'); var ySplit = x.Split(';'); return string.Compare(xSplit[0], ySplit[0], StringComparison.InvariantCultureIgnoreCase) == 0 && string.Compare(xSplit[14], ySplit[14], StringComparison.InvariantCultureIgnoreCase) == 0; } public int GetHashCode(string obj) { return -1; } } При этом основной код сведется к следующему: var lst1 = File.ReadAllLines(@ "D:\test\1.csv"); var lst2 = new HashSet (File.ReadAllLines(@ "D:\\test\2.csv"), new MySequenceEqualityComparer()); lst2.ExceptWith(lst1); Результат будет в переменной lst2
Комментариев нет:
Отправить комментарий