简体   繁体   English

按项目的子部分对字符串列表进行分组

[英]Group list of strings by subparts of item

Sorry for the ambiguous title... I find explaining my issue difficult - let me know if you need to know more.抱歉标题模棱两可...我发现很难解释我的问题 - 如果您需要了解更多信息,请告诉我。

I've got a list that i'd like to be grouped by part of a string.我有一个列表,我想按字符串的一部分进行分组。 This string is also in the list.该字符串也在列表中。 This is the complete list, its not static, and will contain different values.这是完整列表,它不是 static,并且将包含不同的值。

CookieMaker_TransportSettingsManual
CookieMaker_TransportSettingsParameters
Cookie_WrapperSettings
Cookie_WrapperSettingsManual
Cookie_WrapperSettingsParameters
Cookie_ProfileBendSettings
Cookie_ProfileBendSettingsParameters
Cookie_HopperSettings
Cookie_HopperSettingsManual
Cookie_HopperSettingsParameters
Cookie_CutterSettings
Cookie_CutterSettingsManual
Cookie_CutterSettingsParameters
General_SpeedSetting
General_SpeedSettingManual
General_SpeedSettingSettings
General_CalibrationSettings
General_CalibrationSettingsCalibration
Bonbon_Vertical
Bonbon_VerticalAligner
Bonbon_VerticalHopper
Bonbon_VerticalManual
Bonbon_VerticalTransporter
Bonbon_Horizontal
Bonbon_HorizontalHopper
Bonbon_HorizontalManual
Bonbon_HorizontalCookie
Bonbon_HorizontalTransporter
Bonbon_Bonbon
Bonbon_BonbonExhaust
Bonbon_BonbonManual
Bonbon_BonbonSection1
Bonbon_BonbonSection2
Bonbon_BonbonSection3
Bonbon_Compensator
Bonbon_CompensatorCarriage
Bonbon_CompensatorHopper
Bonbon_CompensatorManual
Bonbon_CollectingUnit
Bonbon_CollectingUnitManual
Bonbon_CollectingUnitTransporter
Bonbon_CollectingUnitTubeMaker
CookieMaker_TransportSettings
CookieMaker_TransportSettingsBonbon
CookieMaker_TransportSettingsPandora

The expected result would be a groups like so:预期的结果将是这样的组:

General_SpeedSetting
  ==> General_SpeedSettingManual
  ==> General_SpeedSettingSettings

Cookie_WrapperSettings
  ==> Cookie_WrapperSettingsManual
  ==> Cookie_WrapperSettingsParameters

The resulting datatype does not matter.结果数据类型无关紧要。 Also i don't mind linq.我也不介意 linq。

Code / fiddle to get up and running quickly;代码/小提琴快速启动和运行;

using System;
                    
public class Program
{
    public static void Main()
    {
        var inputString = "CookieMaker_TransportSettingsManual|CookieMaker_TransportSettingsParameters|Cookie_WrapperSettings|Cookie_WrapperSettingsManual|Cookie_WrapperSettingsParameters|Cookie_ProfileBendSettings|Cookie_ProfileBendSettingsParameters|Cookie_HopperSettings|Cookie_HopperSettingsManual|Cookie_HopperSettingsParameters|Cookie_CutterSettings|Cookie_CutterSettingsManual|Cookie_CutterSettingsParameters|General_SpeedSetting|General_SpeedSettingManual|General_SpeedSettingSettings|General_CalibrationSettings|General_CalibrationSettingsCalibration|Bonbon_Vertical|Bonbon_VerticalAligner|Bonbon_VerticalHopper|Bonbon_VerticalManual|Bonbon_VerticalTransporter|Bonbon_Horizontal|Bonbon_HorizontalHopper|Bonbon_HorizontalManual|Bonbon_HorizontalCookie|Bonbon_HorizontalTransporter|Bonbon_Bonbon|Bonbon_BonbonExhaust|Bonbon_BonbonManual|Bonbon_BonbonSection1|Bonbon_BonbonSection2|Bonbon_BonbonSection3|Bonbon_Compensator|Bonbon_CompensatorCarriage|Bonbon_CompensatorHopper|Bonbon_CompensatorManual|Bonbon_CollectingUnit|Bonbon_CollectingUnitManual|Bonbon_CollectingUnitTransporter|Bonbon_CollectingUnitTubeMaker|CookieMaker_TransportSettings|CookieMaker_TransportSettingsBonbon|CookieMaker_TransportSettingsPandora";
        var inputList = inputString.Split('|');
        
        var result = inputList; // Code here ;)
                        
        foreach(var r in result)
        { Console.WriteLine(r);}
    }
}

https://dotnetfiddle.net/neCUEL https://dotnetfiddle.net/neCUEL

What about something like this?这样的事情呢?

    using System;
    using System.Collections.Generic;
    using System.Linq;
                        
    public class Program
    {
        static List<string> myList = new List<string>(){
            "CookieMaker_TransportSettingsManual",
            "CookieMaker_TransportSettingsParameters",
            "Cookie_WrapperSettings",
            "Cookie_WrapperSettingsManual",
            "Cookie_WrapperSettingsParameters",
            "Cookie_ProfileBendSettings",
            "Cookie_ProfileBendSettingsParameters",
            "Cookie_HopperSettings",
            "Cookie_HopperSettingsManual",
            "Cookie_HopperSettingsParameters",
            "Cookie_CutterSettings",
            "Cookie_CutterSettingsManual",
            "Cookie_CutterSettingsParameters",
            "General_SpeedSetting",
            "General_SpeedSettingManual",
            "General_SpeedSettingSettings",
            "General_CalibrationSettings",
            "General_CalibrationSettingsCalibration",
            "Bonbon_Vertical",
            "Bonbon_VerticalAligner",
            "Bonbon_VerticalHopper",
            "Bonbon_VerticalManual",
            "Bonbon_VerticalTransporter",
            "Bonbon_Horizontal",
            "Bonbon_HorizontalHopper",
            "Bonbon_HorizontalManual",
            "Bonbon_HorizontalCookie",
            "Bonbon_HorizontalTransporter",
            "Bonbon_Bonbon",
            "Bonbon_BonbonExhaust",
            "Bonbon_BonbonManual",
            "Bonbon_BonbonSection1",
            "Bonbon_BonbonSection2",
            "Bonbon_BonbonSection3",
            "Bonbon_Compensator",
            "Bonbon_CompensatorCarriage",
            "Bonbon_CompensatorHopper",
            "Bonbon_CompensatorManual",
            "Bonbon_CollectingUnit",
            "Bonbon_CollectingUnitManual",
            "Bonbon_CollectingUnitTransporter",
            "Bonbon_CollectingUnitTubeMaker",
            "CookieMaker_TransportSettings",
            "CookieMaker_TransportSettingsBonbon",
            "CookieMaker_TransportSettingsPandora"
        };
        
        static Dictionary<string, List<string>> results = new Dictionary<string, List<string>>();
        
        //-------------------------------------------------------------------------//
        
        public static void Main()
        {
            
            var orderedList = myList.OrderBy(i=>i).ToList();    
            
            int i = 0;      
            while(i < myList.Count){
            
                var prefix = orderedList[i];
                
                results[prefix] = new List<string>();
                
                if(++i >= orderedList.Count) break;
                            
                while(orderedList[i].StartsWith(prefix)){
                        
                    results[prefix].Add(orderedList[i]);
                    i++;
                    
                    if(i >= orderedList.Count) {                    
                        Print();
                        return;
                    }
                    
                }//while
                
            }//while
            
            Print();
            
        }//main
        
        //-------------------------------------------------------------------------//
        
        private static void Print(){
            foreach (string prefix in results.Keys)
            {
                Console.WriteLine($"Prefix =>{prefix} - {results[prefix].Count}");
                    foreach (string result in results[prefix])
                    {
                        Console.WriteLine($" ======>{result}");
                    }//foreach;
            }//foreach
        }//Print
        

    }//Cls

Fiddle: https://dotnetfiddle.net/GTI4vV小提琴: https://dotnetfiddle.net/GTI4vV

I'm surprised you accepted a solution that pre-sorted the items.我很惊讶您接受了对物品进行预分类的解决方案。 When I tried that, the Bonbon sections got terribly messed up.当我尝试这样做时, Bonbon部分变得非常混乱。

My solution is a bit hacky - to get this to work the way I think you want it took a lot of special cases (and fixing off-by-one issues).我的解决方案有点老套——要让它按照我认为你想要的方式工作需要很多特殊情况(并解决一个问题)。

The code takes care of this kind of pattern:代码处理了这种模式:

CookieMaker_TransportSettingsManual
CookieMaker_TransportSettingsParameters

extracting CookieMaker_TransportSettings and putting both entries under it.提取CookieMaker_TransportSettings并将两个条目放在它下面。 It also copes with the fact that you have CookieMaker_TransportSettings at the beginning and the end of the file.它还可以处理文件开头和结尾处都有 CookieMaker_TransportSettings 的事实。

It also handles this:它也处理这个:

Bonbon_BonbonSection1
Bonbon_BonbonSection2
Bonbon_BonbonSection3

Figuring that you want the three of those to be part of the Bonbon_Bonbon section and not a new Bonbon_BonbonSection section with three entries (1, 2 and 3).确定您希望其中三个成为Bonbon_Bonbon部分的一部分,而不是包含三个条目(1、2 和 3)的新Bonbon_BonbonSection部分。

It also deals with all the Cookie** and Bonbon** sections.它还处理所有 Cookie** 和 Bonbon** 部分。

Here's the main code:这是主要代码:

 //get all the strings from somewhere 
 var inputStrings = File.ReadAllLines("DataFile.txt");

 string lastTitle = null;
 var results = new Dictionary<string, List<string>>();
 string veryLastItem = string.Empty;

 var currentItems = new List<string>();
 for (var i = 0; i < inputStrings.Length - 1; ++i)
 {
     var commonPrefix = FindLongestCommonPrefix(inputStrings[i], inputStrings[i + 1]);
     if (string.IsNullOrEmpty(commonPrefix) || (!string.IsNullOrEmpty(lastTitle) && commonPrefix != lastTitle))
     {
         if (string.IsNullOrEmpty(lastTitle))
         {
             throw new Exception("This isn't going to work - you need to have at least two common things in a row");
         }
         if (inputStrings[i].StartsWith(lastTitle) && inputStrings[i] != lastTitle)
         {
             currentItems.Add(inputStrings[i]);
         }
         AddResultsToDictionary(results, lastTitle, currentItems);
         currentItems = new List<string>();
     }

     if (commonPrefix != inputStrings[i] &&
         ((commonPrefix == lastTitle && commonPrefix != inputStrings[i]) ||
          (!string.IsNullOrEmpty(commonPrefix) && inputStrings[i].StartsWith(commonPrefix))))
     {
         currentItems.Add(inputStrings[i]);
     }

     lastTitle = commonPrefix;
     veryLastItem = inputStrings[i + 1];
 }

 //ok, we're out of the loop:
 //add the last item to the current list
 currentItems.Add(veryLastItem);

 //and add the last set of items to the dictionary
 if (lastTitle != null)
 {
     AddResultsToDictionary(results, lastTitle, currentItems);
 }

 foreach (var result in results)
 {
     Debug.WriteLine(result.Key);
     foreach (var item in result.Value)
     {
         Debug.WriteLine($"  ==> {item}");
     }
 }

 void AddResultsToDictionary(Dictionary<string, List<string>> dictionary, string s, List<string> list)
 {
     if (dictionary.TryGetValue(s, out var existingList))
     {
         existingList.AddRange(list);
     }
     else
     {
         dictionary.Add(s, list);
     }
 }

} }

And it calls this function to determine the section headings:它调用这个 function 来确定章节标题:

private string FindLongestCommonPrefix(string s1, string s2)
{
    var minLen = Math.Min(s1.Length, s2.Length);
    for (var i = 0; i < minLen; ++i)
    {
        if (s1[i] != s2[i])
        {
            if (i == 0)
            {
                return string.Empty;
            }
            else
            {
                //if the common part is not s1, we need to find the last place where the following
                //   the last letter of the common part is a lower case letter followed by either
                //   an underscore or a capital letter
                if (i == s1.Length)
                {
                    return s1;
                }

                if (s1[i] == '_' || s1[i - 1] == '_' || s2[i] == '_' || s2[i - 1] == '_')
                {
                    return string.Empty;
                }

                for (var j = i; j > 0; --j)
                {
                    if (char.IsLower(s1[j-1]) && (char.IsUpper(s1[j]) /*|| s1[j] == '_'*/))
                    {
                        return s1.Substring(0, j);
                    }
                }
                //I shouldn't get here, but, if I do
                return string.Empty;
            }
        }
    }
    //otherwise
    return s1.Substring(0, minLen);
}

The result ends up looking like:结果最终看起来像:

CookieMaker_TransportSettings
  ==> CookieMaker_TransportSettingsManual
  ==> CookieMaker_TransportSettingsParameters
  ==> CookieMaker_TransportSettingsBonbon
  ==> CookieMaker_TransportSettingsPandora
Cookie_WrapperSettings
  ==> Cookie_WrapperSettingsManual
  ==> Cookie_WrapperSettingsParameters
Cookie_ProfileBendSettings
  ==> Cookie_ProfileBendSettingsParameters
Cookie_HopperSettings
  ==> Cookie_HopperSettingsManual
  ==> Cookie_HopperSettingsParameters
Cookie_CutterSettings
  ==> Cookie_CutterSettingsManual
  ==> Cookie_CutterSettingsParameters
General_SpeedSetting
  ==> General_SpeedSettingManual
  ==> General_SpeedSettingSettings
General_CalibrationSettings
  ==> General_CalibrationSettingsCalibration
Bonbon_Vertical
  ==> Bonbon_VerticalAligner
  ==> Bonbon_VerticalHopper
  ==> Bonbon_VerticalManual
  ==> Bonbon_VerticalTransporter
Bonbon_Horizontal
  ==> Bonbon_HorizontalHopper
  ==> Bonbon_HorizontalManual
  ==> Bonbon_HorizontalCookie
  ==> Bonbon_HorizontalTransporter
Bonbon_Bonbon
  ==> Bonbon_BonbonExhaust
  ==> Bonbon_BonbonManual
  ==> Bonbon_BonbonSection1
  ==> Bonbon_BonbonSection2
  ==> Bonbon_BonbonSection3
Bonbon_Compensator
  ==> Bonbon_CompensatorCarriage
  ==> Bonbon_CompensatorHopper
  ==> Bonbon_CompensatorManual
Bonbon_CollectingUnit
  ==> Bonbon_CollectingUnitManual
  ==> Bonbon_CollectingUnitTransporter
  ==> Bonbon_CollectingUnitTubeMaker

Try following:尝试以下操作:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.txt";
  
         static void Main(string[] args)
         {
             List<string> lines = File.ReadLines(FILENAME).ToList();
             lines = lines.OrderBy(x => x).ToList();

             List<Group> groups = new List<Group>();
             Group group = new Group();
             groups.Add(group);
             group.basename = lines[0].Trim();
             List<List<string>> results = new List<List<string>>();
             for (int i = 2; i < lines.Count; i++)
             {
                 string line = lines[i].Trim();
                 if (!line.StartsWith(group.basename))
                 {
                     group = new Group();
                     groups.Add(group);
                     group.basename = line;
                 }
                 else
                 {
                     if(group.values == null) group.values = new List<string>();
                     group.values.Add(line.Substring(group.basename.Length));
                 }
             }
 
         }
    }
    public class Group
    {
        public string basename { get; set; }
        public List<string> values { get; set; }
    }

  
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM