Есть ли способ сортировки нескольких значений и сохранения только наибольшего из них? - PullRequest
2 голосов
/ 25 марта 2019

У меня есть некоторый код, выдающий выходные данные типа местоположения и максимального количества.

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

Данные в настоящее время генерируются и хранятся в виде списка строк, которые хранятся с пробелами между местоположениями.

var locationOutputDistinct = new List<string>
{
"PERSON:2 ",
"PERSON:1 SCOOTER:1 ",
"PERSON:1 BIKE:1 ",
"PERSON:2 BIKE:1 ",
"PERSON:1 BIKE:1 SCOOTER:1 ",
"PERSON:3 ",
"PERSON:3 BIKE:1 ",
"PERSON:4 ",
"PERSON:4 BIKE:1 ",
"PERSON:2 SCOOTER:1 ",
"PERSON:5 ",
"PERSON:3 SCOOTER:1 ",
"PERSON:6 "
};

Идеальновывод будет выглядеть примерно так:

PERSON:1 BIKE:1 SCOOTER:1 
PERSON:4 BIKE:1 
PERSON:3 SCOOTER:1 
PERSON:6 

Указывает, что у вас может быть 6 человек, или 3 человека, и скутер, или 4 человека и велосипед, или 1 человек, 1 велосипед и 1 скутер.Каждая самая большая комбинация их соответствующих типов.

Каков наилучший подход к этому?

Вот некоторые другие наборы данных и ожидаемые результаты.https://justpaste.it/6cce5

Вот наглядный пример и объяснение того, что я пытаюсь произвести.https://justpaste.it/4vqu2

Ответы [ 4 ]

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

На основе 2 классов

public class Res{
    public string Key;
    public int Val;
}
public class Reduced
{
    public string Key;
    public IEnumerable<Res>[] invalid;
}

Вы можете использовать этот код для агрегирования результатов.

    var loc =
   locationOutputDistinct
   .Select(x => x.Trim().Split(' ').ToArray()
   .Select(
       y => {
           var kv = y.Split(':');
           return new Res()
           {
               Key = kv[0],
               Val = int.Parse
                (kv[1])
           };
       }).ToArray()).ToArray()

    .GroupBy(x =>
       String.Join(",",
       x.Select(y => y.Key))
       );

Тогда вы должны применить правило сокращения

    var reduced = 
    loc.Select(x => new Reduced() {
    Key = x.Key,                 
            invalid =
                x.Where(y => loc.Any(ext => ext.Any(xsup =>
                   y.All(z => xsup.Any(xsupcell => xsupcell.Key == z.Key)) &&
                   y.All(z =>
                   z.Val <= xsup
                         .Where(xsupcell =>
                   xsupcell.Key == z.Key)
                         .First().Val) &&
                         ((xsup.Length > y.Length) ||
                   y.Any(z =>
                   z.Val < xsup
                         .Where(xsupcell =>
                   xsupcell.Key == z.Key)
                         .First().Val))

                    ))).ToArray()
                            });

    var remain = loc.Select(x => x.Where(y =>
    !reduced.Where(r => r.Key == x.Key).SelectMany(r2 => r2.invalid).Any(r3 => r3.All(r4 =>
    y.Any(y2 => y2.Key == r4.Key && y2.Val == r4.Val)))));

Из вашего последнего примера я вижу, что вы хотите уменьшить также менее расширенные комбинации y с более расширенными (xsup.Length > y.Length), поэтому я включил эту последнюю функцию в приведенный выше код.

На этом этапе вам нужно развернуть и распечатать результаты.

    foreach (var x in remain)
        Console.WriteLine
        (//x.Key+": " +
          String.Join("\n",
          x.Select(y =>
          String.Join(" ",
          y.Select(z => z.Key + ":" + z.Val)
              ))));

Из оригинальной последовательности в вашем вопросе вы получите

PERSON:6
PERSON:3 SCOOTER:1
PERSON:4 BIKE:1
PERSON:1 BIKE:1 SCOOTER:1

и от

"AM:4 SC:1 ",
"AM:4 WC:1 ",
"AM:4 WC:1 SC:1 ",
"AM:4 WC:2 ",
"AM:4 WC:2 SC:1 ",
"AM:5 ",
"AM:5 WC:1 ",
"AM:6 ",
"AM:6 WC:1 ",
"AM:7 ",
"AM:8 "

вы получите

AM:6 WC:1
AM:4 WC:2 SC:1
AM:8

Скрипка здесь .

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

Я предоставляю это решение, зная, что мои выходные данные не соответствуют вашим выходным данным.Это потому, что я не согласен с вашим выводом.

Например, один ваш выходной сэмпл обеспечивает следующее:

OUTPUT:
AM:10 BK:1 
AM:8 BK:1 BWC:1 
AM:8 BK:1 WC:1 
AM:7 BK:1 SC:1
AM:6 BK:1 BWC:2 
AM:6 BK:1 BWC:1 WC:1 
AM:6 BK:1 WC:2 
AM:5 BK:1 BWC:1 SC:1
AM:5 BK:1 WC:1 SC:1
AM:4 BK:1 BWC:2 WC:1 
AM:4 BK:1 WC:3 
AM:4 BK:1 SC:2
AM:2 BK:1 WC:1 SC:2

Но AM:4 BK:1 WC:3 меньше AM:8 BK:1 WC:1, поэтому я неЯ считаю, что оба из них должны быть результатом, и есть множество примеров этого.При этом, вот мое решение, которое получает абсолютный максимум для каждой возможной комбинации ввода.

Выполнение:

        // Get all the `keys` in the location output
        var keys = GetKeys(locationOutputDistinct);
        // Get the possible combinations of keys
        var combinations = GetCombination(keys);

        var maxCombos = new List<string>();

        foreach (var combo in combinations)
        {
            var maxOfCombo = GetMaxOfCombo(locationOutputDistinct, combo);
            if (maxOfCombo.Any())
            {
                maxCombos.AddRange(maxOfCombo);
            }
        }

        maxCombos = maxCombos.Distinct().ToList();
        maxCombos.Sort();
        foreach (var maxCombo in maxCombos)
        {
            Console.WriteLine(maxCombo);
        }

И вспомогательные методы:

        /// <summary>
    /// Get the keys from the input and return it in a `;` delimited string
    /// </summary>
    /// <param name="vals">Input list</param>
    /// <returns></returns>
    public static List<string> GetKeys(List<string> vals)
    {
        var keys = new List<string>();
        foreach (var grouping in vals)
        {
            var parameters = grouping.Split(' ');
            foreach (var parameter in parameters)
            {
                var paramVals = parameter.Split(':');
                var label = paramVals[0].Trim();
                if (!string.IsNullOrEmpty(label) && !keys.Contains(label))
                {
                    keys.Add(label);
                }
            }
        }

        return keys;
    }

    /// <summary>
    /// Take all the keys and return all possible combinations of them
    /// </summary>
    /// <param name="list">List of keys</param>
    /// <returns></returns>
    public static List<string> GetCombination(List<string> list)
    {
        var combos = new List<string>();
        var comb = "";
        double count = Math.Pow(2, list.Count);
        for (int i = 1; i <= count - 1; i++)
        {
            var sep = "";
            string str = Convert.ToString(i, 2).PadLeft(list.Count, '0');
            for (int j = 0; j < str.Length; j++)
            {
                if (str[j] == '1')
                {
                    comb += sep + list[j];
                    sep = ";";
                }
            }

            combos.Add(comb);
            comb = "";
        }

        return combos;
    }

    /// <summary>
    /// Return the combinations that have the largest values
    /// </summary>
    /// <param name="source">String list of input</param>
    /// <param name="combo">`;` delimited keys you're checking</param>
    /// <returns></returns>
    public static List<string> GetMaxOfCombo(List<string> source, string combo)
    {
        var comboKeys = combo.Split(';');
        var matchingCombos = source.Where(s => comboKeys.All(a => s.IndexOf(a) != -1) && s.Trim().Split(' ').Length == comboKeys.Count());

        // Return empty set if there were none
        if (!matchingCombos.Any())
        {
            return new List<string>();
        }

        var comboValues = matchingCombos.Select(mc => new
        {
            // Get the value sum of the individual keys
            value = mc.Split(' ').Where(w => !string.IsNullOrEmpty(w.Trim())).Sum(s => int.Parse(string.Concat(s.Where(c => char.IsDigit(c))))),
            label = mc
        });
        // Get the max value
        var max = comboValues.Max(m => m.value);
        // Get all that have the same max
        var maxCombos = comboValues.Where(f => f.value == max);

        if (!maxCombos.Any())
        {
            return new List<string>();
        }
        return maxCombos.Select(s => s.label).ToList();
    }

Вот рабочий пример: https://dotnetfiddle.net/mwpkMO

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

Приведенные ниже запросы LINQ дадут вам ожидаемый результат.Сначала мы группируем по именам без цифр, так как они всегда будут одинаковыми.Затем мы выполняем итерацию по группировкам и вычисляем максимальную сумму, с которой нам нужно соответствовать, и, наконец, получаем запись из нашей группы с соответствующей суммой.

var groupedLocation = locationOutputDistinct.GroupBy(x => new string(x.Where(c => !char.IsDigit(c)).ToArray()))
                                            .OrderByDescending(x => x.Key.Length)
                                            .ToList();

foreach(var group in groupedLocation)
{
    var maxSum = group.Max(x => x.Where(c => char.IsDigit(c))
                      .Select(z => char.GetNumericValue(z)).Sum());
    var matchingRecord = group.FirstOrDefault(x => x.Where(c => char.IsDigit(c)).Select(z => char.GetNumericValue(z)).Sum() == maxSum);

    Console.WriteLine(matchingRecord);
}

OUTPUT

enter image description here

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

работают следующие работы:

            var locationOutputDistinct = new List<string>
            {
                "FRNT:2 REAR:1 ",
                "FRNT:2 ROOF:1 ",
                "FRNT:2 ROOF:1 REAR:1 ",
                "FRNT:3 ",
                "FRNT:3 REAR:1 ",
                "FRNT:4 ",
                "FRNT:4 REAR:1 ",
                "FRNT:3 ROOF:1 ",
                "FRNT:5 ",
                "FRNT:4 ROOF:1 ",
                "FRNT:6 ",
                "FRNT:5 ROOF:1 ",
                "FRNT:7 ",
                "FRNT:6 ROOF:1 ",
                "FRNT:8 "
            };
            var parsedData = locationOutputDistinct
                .Select(x => x.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(y => new KeyValuePair<string, int>(y.Substring(0, y.IndexOf(":")), int.Parse(y.Substring(y.IndexOf(":") + 1)))).OrderBy(z => z.Key).ToList())
                    .ToList();

            var groups = parsedData
                .GroupBy(x => string.Join("^", x.Select(y => y.Key)))
                .ToList();

            var results = groups.Select(x => x.SelectMany(y => y).GroupBy(z => z.Key).Select(z => new { Key = z.Key, Value = z.Max(a => a.Value)}).ToList()).ToList();
            List<string> strings = results.Select(x => string.Join(" ", x.Select(y => y.Key + ":" + y.Value.ToString()))).ToList();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...