Упорядочить список отсортированного набора строк по количеству, а затем по строкам

#c#

#c#

Вопрос:

У меня есть пользовательский класс, называемый PairString

  public class PairString: IComparer<PairString>
    {
        public string first;
        public string second;
        public PairString(string f, string s)
        {
            first = f;
            second = s;
        }
        public int Compare([AllowNull] PairString x, [AllowNull] PairString y)
        {
            if (x == null || y == null) return -1;
            var f = string.Compare(x.first, y.first);
            var s = string.Compare(x.second, y.second);
            return f == s ? s : f;
        }
    }
  

Я хочу создавать группы по количеству, а затем по лексическому порядку строк в этих группах из списка входных парных строк List. Приведенный ниже метод выполняет группировку правильно. Но когда я пытаюсь отсортировать группы в лексическом порядке для групп с равным количеством, выдается сообщение «По крайней мере, один объект должен реализовать ошибку IComparer»

 public static List<string> MaxItemAssociatoinGroup(List<PairString> input)
        {
            if (input == null || input.Count == 0) return null;
            List<SortedSet<string>> output = new List<SortedSet<string>>();
            foreach (var item in input)
            {
                if (output.Any(x => x.Contains(item.first) || x.Contains(item.second)))
                {
                    //Take the set containing one or two or both items
                    var set1 = output.FirstOrDefault(x => x.Contains(item.first));
                    var set2 = output.FirstOrDefault(x => x.Contains(item.second));
                    if (set1 == null)
                        set2.UnionWith(new SortedSet<string> { item.first, item.second });

                    else if (set2 == null)
                        set1.UnionWith(new SortedSet<string> { item.first, item.second });

                    else if (set1 != set2)
                    {
                        set1.UnionWith(set2);
                        output.Remove(set2);
                    }
                }
                else
                    output.Add(new SortedSet<string>(new List<string>() { item.first, item.second }));
            }
            var maxlistAssociation = output.OrderByDescending(x => x.Count).First();
            return new List<string>(maxlistAssociation);
        }
  

Я не уверен, как добиться лексического порядка для групп с одинаковым количеством,
Пример ввода

 new PairString("item3","item4"),
            new PairString("item3","item6"),
            new PairString("item5","item6"),
            new PairString("item2","item8"),
            new PairString("item8","item9"),
            new PairString("item1","item2")
  

он группируется в 2 группы с равным количеством {item3,item4,item5,item6} amp; {item1,item2,item8,item9} , но возвращается {item3,item4,item5,item6} первым в списке. но я хочу вторую группу, поскольку она содержит элемент, который лексикографически первый, чем первая группа. чего мне здесь не хватает?

Комментарии:

1. if (x == null || y == null) return -1; не собирается обеспечивать согласованный порядок сортировки null объектов (т.е. {"value", null} и {null, "value"} будут упорядочены по-разному друг от друга). Обычно вы возвращали бы, -1 если один был null и 1 если другой был null , в зависимости от того, хотите ли вы, чтобы null элементы были отсортированы в начало или в конец.

2. Это хороший ввод. Я рассмотрю возможность его изменения

3. Да, я действительно провел тест, и var x = new[] {"foo", null, "bar"}.OrderBy(i => i, new PairString()); (где я использую аналогичную логику, но просто сравниваю строки) получается бесконечный цикл, потому что null и строковые значения продолжают меняться местами на каждой итерации!

4. Кстати, я нигде не вижу, где вы пытаетесь отсортировать группы по лексическому порядку. Можете ли вы показать этот код?

5.Да, извините, это было в моем предыдущем коде, и, пробуя что-то, я убрал это. Здесь это для maxlistassociation var maxlistAssociation = output.OrderByDescending(x => x.Count).ThenBy(x=>x)First();

Ответ №1:

Похоже, вам не хватает метода, который будет сравнивать два SortedSet<string> объекта и возвращать тот, который стоит первым лексически. Один из способов сделать это — сравнить каждый элемент из одного набора с соответствующим в другом наборе и вернуть первое неравнозначное сравнение:

 public class SortedSetComparer<T> : IComparer<SortedSet<T>> where T : IComparable<T>
{
    public int Compare(SortedSet<T> x, SortedSet<T> y)
    {
        // Null checks
        if (x == null) return y == null ? 0 : 1;
        if (y == null) return -1;

        var minCount = Math.Min(x.Count, y.Count);

        // Compare each item from one set with the corresponding one in the other set
        for (var i = 0; i < minCount; i  )
        {
            var result = x.ElementAt(i).CompareTo(y.ElementAt(i));

            // Return the first non-equal result
            if (result != 0) return resu<
        }

        // If all the items were equal, return the comparison of the Count
        return x.Count.CompareTo(y.Count);
    }
}
  

Затем мы можем упорядочить наши результаты (после сортировки по размеру), передав экземпляр этого класса ThenBy методу:

 var maxlistAssociation = output
    .OrderByDescending(x => x.Count)
    .ThenBy(x => x, new SortedSetComparer<string>())
    .First();
  

В зависимости от поведения, которое вы хотите получить от этого метода, мы могли бы также включить порядок по Count в наш метод сравнения, чтобы он сначала помещал наборы с наибольшим количеством элементов, а затем сортировал их по алфавиту:

 public class SortedSetComparer<T> : IComparer<SortedSet<T>> where T : IComparable<T>
{
    public int Compare(SortedSet<T> x, SortedSet<T> y)
    {
        // Null checks
        if (x == null) return y == null ? 0 : 1;
        if (y == null) return -1;

        // Compare the counts first, in descending order
        var countComparison = x.Count.CompareTo(y.Count);
        if (countComparison != 0) return countComparison * -1;

        // Then compare each item from one set lecially 
        // with the corresponding one in the other set
        return x.Select((item, index) =>
            x.ElementAt(index).CompareTo(y.ElementAt(index)))
            .FirstOrDefault(result => result != 0);
    }
}
  

И теперь нам нужно только одно OrderBy предложение:

 var maxlistAssociation = output
    .OrderBy(x => x, new SortedSetComparer<string>())
    .First();