2011-03-31 80 views
3

我想按照其首字母对字符串排序的列表进行分组。比方说,这是列表:按文化意识初始字母组字符串

azaroth 
älgkebab 
orgel 
ölkorv 

当列表根据sv-SE排序,这是排序顺序:

azaroth 
orgel 
älgkebab 
ölkorv 

这意味着按首字母分组将

A 
    azaroth 
O 
    orgel 
Ä 
    älgkebab 
Ö 
    ölkorv 

这是有道理的,这也是你如何在一个使用sv-SE的国家的电话簿中找到它。

当列表根据en-US排序,这是排序顺序:

älgkebab 
azaroth 
ölkorv 
orgel 

现在到了有趣的部分。这意味着,按首字母分组将

AÄ 
    älgkebab 
    azaroth 
OÖ 
    ölkorv 
    orgel 

由于所有的实际目的,“A”和“A”分别为相同的字母排序中治疗,所以是“o”和“O”这意味着他们为此目的相同的初始。这是AFAIK如何在一个使用en-US的国家/地区的电话簿中找到它。

我的问题是,如何以编程方式实现这个分组,当它因文化而异?或换句话说,您如何知道在特定文化中排列列表时,哪些字母被视为“相同”?

例如,对于“a”与“ä”,我还没有找到使StringComparer返回0的方法。

我,似乎工作的解决方案,它做到这一点:

if (
    cultureInfo.CompareInfo.GetSortKey("a").KeyData[1] == 
    cultureInfo.CompareInfo.GetSortKey("ä").KeyData[1] 
) // same initial (this will return false for sv-SE and true for en-US) 

问题是,我不知道它是否适用于任何文化,甚至什么在第二个数据片KeyData实际上是SortKey的数组。 page on MSDN比较模糊,可能有目的地如此。所以我宁愿有一个更可靠的解决方案。

回答

1

当您在sv-SE中比较aä时,结果为-1,因此如果两个单词相同,除了变音符以外,它们始终排序相同。但是你仍然可以看出他们的排序方式是相同的:将一些字符追加到另一个字符上,然后对其进行不同的排序,然后进行比较。然后切换添加的字符并再次比较。如果结果不同,则字符排序相同。

实施例:

sv-SE: 
"a0" < "ä1" 
"a1" < "ä0" 
en-US: 
"a0" < "ä1" 
"a1" > "ä0" 

因此,在sv-SE'a' < 'ä',但在en-US'a' == 'ä'。以下是根据这些规则对字符串列表进行分组的类。但对于某些文化而言,它不适用,因为它们的排序顺序更为复杂。例如在捷克,ch被视为一个单独的字母,按h排序。我不知道你会如何解决这个问题。

另外,代码使用01作为要附加的字符。如果有些文化中这些角色不会影响排序,那么这种方式就行不通。

class Grouper 
{ 
    StringComparer m_comparer; 

    public Grouper(StringComparer comparer) 
    { 
     m_comparer = comparer; 
    } 

    public List<Tuple<string, List<string>>> Group(IEnumerable<string> strings) 
    { 
     List<Tuple<string, List<string>>> result = 
      new List<Tuple<string, List<string>>>(); 

     var sorted = strings.OrderBy(s => s, m_comparer); 

     string previous = null; 

     List<char> currentGroupName = null; 
     List<string> currentGroup = null; 

     foreach (var s in sorted) 
     { 
      char sInitial = ToUpper(s[0]); 
      if (currentGroup == null || !AreEqual(s[0], previous[0])) 
      { 
       if (currentGroup != null) 
        result.Add(Tuple.Create(
         SortGroupName(currentGroupName), 
         currentGroup)); 
       currentGroupName = new List<char> { sInitial }; 
       currentGroup = new List<string> { s }; 
      } 
      else 
      { 
       if (!currentGroupName.Contains(sInitial)) 
        currentGroupName.Add(sInitial); 
       currentGroup.Add(s); 
      } 

      previous = s; 
     } 

     if (currentGroup != null) 
      result.Add(Tuple.Create(SortGroupName(currentGroupName), currentGroup)); 

     return result; 
    } 

    string SortGroupName(List<char> chars) 
    { 
     return new string(chars.OrderBy(c => c.ToString(), m_comparer).ToArray()); 
    } 

    bool AreEqual(char c1, char c2) 
    { 
     return Math.Sign(m_comparer.Compare(c1 + "0", c2 + "1")) == 
      -Math.Sign(m_comparer.Compare(c1 + "1", c2 + "0")); 
    } 

    char ToUpper(char c) 
    { 
     return c.ToString().ToUpper()[0]; 
    } 
} 

而且,这个类是远离生产质量,例如,它不能处理null S或空字符串。

+0

有趣。这与我目前使用的非常相似,除了使用'SortKey'的'AreEqual'。您的'AreEqual'是一个优雅的方式,使得“StringComparer为'a'和'ä'返回0',就像我在问题中写的那样。 :) – bzlm 2011-04-01 07:19:12

0

它可能是一个实现内部值,类似于常量。价值本身并不重要,只是它与其他相关价值的比较。

这与(例如)C中的EOF值类似。虽然GCC将其定义为-1,但实际值可能会有所不同,因此最终开发人员代码应仅比较值,而不会对其进行评估。

+0

的确如此。我正在寻找一种解决方案来解决不依赖于实现细节的常规分组问题。 – bzlm 2011-04-01 07:20:13