识别字符串集合中的类似项目并对其进行分组

时间:2015-07-08 17:15:04

标签: c# .net linq

我有一系列字符串,如下所示:

List<string> codes = new List<string>
{
    "44.01", "44.02", "44.03", "44.04", "44.05", "44.06", "44.07", "44.08", "46", "47.10"
};

每个字符串由两个以句号分隔的组件组成 - 前缀代码和子代码。有些字符串没有子代码。

我希望能够组合前缀相同的字符串,并将其输出如下,其他代码也是:

  

44(01,02,03,04,05,06,07,08),46,47.10

我遇到了这个问题的第一个障碍,即如何识别和组合前缀值相同的代码,以便我可以将它们组合成一个字符串,如上所示。

8 个答案:

答案 0 :(得分:5)

你可以这样做:

login()

这是它的工作原理:

  • 拆分分隔符列表中的每个项目,并使用var query = codes.Select(c => new { SplitArray = c.Split('.'), //to avoid multiple split Value = c }) .Select(c => new { Prefix = c.SplitArray.First(), //you can avoid multiple split if you split first and use it later PostFix = c.SplitArray.Last(), Value = c.Value, }) .GroupBy(r => r.Prefix) .Select(grp => new { Key = grp.Key, Items = grp.Count() > 1 ? String.Join(",", grp.Select(t => t.PostFix)) : "", Value = grp.First().Value, }); Prefix和原始Postfix
  • 填充匿名类型
  • 后来value
  • 上的小组
  • 之后使用Prefix
  • 选择值和修补后的值

输出:

string.Join

输出将是:

foreach (var item in query)
{
    if(String.IsNullOrWhiteSpace(item.Items))
        Console.WriteLine(item.Value);
    else
        Console.WriteLine("{0}({1})", item.Key, item.Items);
}

答案 1 :(得分:5)

试试这个: -

 var result = codes.Select(x => new { SplitArr = x.Split('.'), OriginalValue = x })
                   .GroupBy(x => x.SplitArr[0])
                   .Select(x => new 
                    {
                       Prefix= x.Key,
                       subCode = x.Count() > 1 ? 
                             String.Join(",", x.Select(z => z.SplitArray[1])) : "",
                       OriginalValue = x.First().OriginalValue
                    });

您可以按照以下方式打印所需的输出: -

foreach (var item in result)
{
     Console.Write("{0}({1}),",item.Prefix,item.subCode);
}

Working Fiddle.

答案 2 :(得分:1)

概述的想法:

  • 使用Dictionary<string, List<string>>收集结果

  • 在列表中的循环中,使用string.split() ..第一个元素将是您的词典键...如果该键尚不存在,则在那里创建一个新的List<string> < / p>

  • 如果split的结果有第二个元素,请将其附加到List

  • 使用第二个循环将该Dictionary格式化为输出字符串

当然,linq也是可能的,例如

List<string> codes = new List<string>() {
    "44.01", "44.05", "47", "42.02", "44.03" };

var result = string.Join(",",
    codes.OrderBy(x => x)
    .Select(x => x.Split('.'))
    .GroupBy(x => x[0])
    .Select((x) =>
    {
        if (x.Count() == 0) return x.Key;
        else if (x.Count() == 1) return string.Join(".", x.First());
        else return x.Key + "(" + string.Join(",", x.Select(e => e[1]).ToArray()) + ")";
    }).ToArray());

得爱linq ...哈哈......我认为这是一个怪物。

答案 3 :(得分:1)

您可以在一个聪明的LINQ

中完成所有操作
var grouped = codes.Select(x => x.Split('.'))
                   .Select(x => new
                   {
                       Prefix = int.Parse(x[0]),
                       Subcode = x.Length > 1 ? int.Parse(x[1]) : (int?)null
                   })
                   .GroupBy(k => k.Prefix)
                   .Select(g => new
                   {
                       Prefix = g.Key,
                       Subcodes = g.Where(s => s.Subcode.HasValue).Select(s => s.Subcode)
                   })
                   .Select(x =>
                       x.Prefix +
                       (x.Subcodes.Count() == 1 ? string.Format(".{0}", x.Subcodes.First()) :
                        x.Subcodes.Count() > 1 ? string.Format("({0})", string.Join(",", x.Subcodes))
                                                : string.Empty)
                   ).ToArray();
  1. 首先按CodeSubcode
  2. 拆分
  3. 由您Code分组,并将所有Subcode作为集合
  4. 以适当的格式选择
  5. 看看这个问题,我认为你应该在最后一个Select之前停止,并让数据显示在你的应用程序的另一个部分/方法中完成。

答案 4 :(得分:1)

老式的方式:

List<string> codes = new List<string>() {"44.01", "44.05", "47", "42.02", "44.03" };
string output="" 
for (int i=0;i<list.count;i++)
{
  string [] items= (codes[i]+"..").split('.') ;
  int pos1=output.IndexOf(","+items[0]+"(") ;
  if (pos1<0) output+=","+items[0]+"("+items[1]+")" ; // first occurence of code : add it
  else
  { // Code already inserted : find the insert point
    int pos2=output.Substring(pos1).IndexOf(')') ;   
    output=output.Substring(0,pos2)+","+items[1]+output.Substring(pos2) ;
  }
}
if (output.Length>0) output=output.Substring(1).replace("()","") ;

答案 5 :(得分:1)

这将起作用,包括没有子码的正确格式,单个子码,多个子码。它也不假设前缀或子代码是数字的,因此它会保留前导零。你的问题没有说明如果你有一个没有子代码的前缀和子代码的相同前缀,该怎么办,所以它可能不适用于那个边缘情况(44,44.01)。我有它,以便在该边缘情况下忽略没有子代码的前缀。

List<string> codes = new List<string>
{
    "44.01", "44.02", "44.03", "44.04", "44.05", "44.06", "44.07", "44.08", "46", "47.10"
};
var result=codes.Select(x => (x+".").Split('.'))
                   .Select(x => new
                   {
                       Prefix = x[0],
                       Subcode = x[1]
                   })
                   .GroupBy(k => k.Prefix)
                   .Select(g => new
                   {
                       Prefix = g.Key,
                       Subcodes = g.Where(s => s.Subcode!="").Select(s => s.Subcode)
                   })
                   .Select(x =>
                       x.Prefix +
                       (x.Subcodes.Count() == 0 ? string.Empty :
                        string.Format(x.Subcodes.Count()>1?"({0})":".{0}",
                         string.Join(",", x.Subcodes)))
                   ).ToArray();

答案 6 :(得分:0)

一般的想法,但我确定用gcc取代Substring来电也会好很多

Regex

答案 7 :(得分:0)

你可以选择几种方式...我可以看到你制作一个Dictionary<string,List<string>>,这样你就可以将“44”映射到{“。01”,“。02”,“。03的列表“等等}这需要您在将代码添加到此列表之前处理代码(即分离代码的两个部分并处理只有一个部分的情况)。

或者您可以将它们放入SortedSet并提供您自己的比较器,它们知道这些是代码以及如何对它们进行排序(至少比按字母顺序对它们进行分类更可靠)。但是,迭代这个SortedSet仍然需要特殊的逻辑,所以上面的Dictionary to List选项可能仍然是可取的。

在任何一种情况下,您仍然需要处理特殊情况“46”,其中代码中没有第二个元素。在字典示例中,您是否会将String.Empty插入列表?如果你有一个列表{“46”,“46.1”},你不知道你输出了什么 - 你会显示为“46(null,1)”或......“46(0,1)”......或“46(,1)”或“46(1)”?