自定义列表<string []>排序</string []>

时间:2014-09-14 00:32:17

标签: c# string sorting

我有string[]的列表。

List<string[]> cardDataBase;

我需要按自定义顺序按每个列表项的第二个字符串值(item[1])对该列表进行排序。

自定义顺序有点复杂,按起始字符排序:

"MW1"
"FW"
"DN"
"MWSTX1CK"
"MWSTX2FF"

然后按照以上开头的字母顺序排列这些字母:

"A"
"Q"
"J"
"C"
"E"
"I"
"A"

然后按照上面的数字。

示例,左侧无序列表,右侧订购:

MW1E10              MW1Q04
MWSTX2FFI06         MW1Q05
FWQ02               MW1E10
MW1Q04              MW1I06
MW1Q05              FWQ02
FWI01               FWI01
MWSTX2FFA01         DNC03
DNC03               MWSTX1CKC02
MWSTX1CKC02         MWSTX2FFI03
MWSTX2FFI03         MWSTX2FFI06
MW1I06              MWSTX2FFA01

我尝试过Linq,但我现在并不是那么好,而且我自己也无法解决这个问题。我需要一本字典,正则表达式或带有正则表达式的字典吗?什么是最好的方法?

4 个答案:

答案 0 :(得分:1)

我认为您正在接近这个错误。您对 排序字符串进行了排序,您将对错误表示为字符串的结构化对象进行排序(有人恰当地命名为此反模式"stringly typed")。您的要求表明您了解这种结构,但它没有在数据结构List<string[]>中表现出来,并且这使您的生活变得艰难。您应该将该结构解析为实数类型(结构或类),然后对其进行排序。

enum PrefixCode { MW1, FW, DN, MWSTX1CK, MWSTX2FF, }
enum TheseLetters { Q, J, C, E, I, A, }
struct CardRecord : IComparable<CardRecord> {
    public readonly PrefixCode Code;
    public readonly TheseLetters Letter;
    public readonly uint Number;
    public CardRecord(string input) {
        Code = ParseEnum<PrefixCode>(ref input);
        Letter = ParseEnum<TheseLetters>(ref input);
        Number = uint.Parse(input);
    }
    static T ParseEnum<T>(ref string input) { //assumes non-overlapping prefixes
        foreach(T val in Enum.GetValues(typeof(T))) {
            if(input.StartsWith(val.ToString())) {
                input = input.Substring(val.ToString().Length);
                return val;
            }
        }
        throw new InvalidOperationException("Failed to parse: "+input);
    }
    public int CompareTo(CardRecord other) {
        var codeCmp = Code.CompareTo(other.Code);
        if (codeCmp!=0) return codeCmp;
        var letterCmp = Letter.CompareTo(other.Letter);
        if (letterCmp!=0) return letterCmp;
        return Number.CompareTo(other.Number);
    }
    public override string ToString() { 
        return Code.ToString() + Letter + Number.ToString("00");
    }
}

使用上述方法处理您的示例的程序可能是:

static class Program {
    static void Main() {
        var inputStrings = new []{ "MW1E10", "MWSTX2FFI06", "FWQ02", "MW1Q04", "MW1Q05", 
            "FWI01", "MWSTX2FFA01", "DNC03", "MWSTX1CKC02", "MWSTX2FFI03", "MW1I06" };
        var outputStrings = inputStrings
            .Select(s => new CardRecord(s))
            .OrderBy(c => c)
            .Select(c => c.ToString());
        Console.WriteLine(string.Join("\n", outputStrings));
    }
}

这会生成与示例中相同的顺序。在实际代码中,我建议您根据代码的类型命名类型,而不是TheseLetters

此解决方案 - 具有真正的解析步骤 - 非常出色,因为您几乎可以确定您希望在某些时候对此数据执行更多操作,这样您就可以实际访问数据很容易。此外,对于未来的维护者来说,它是可以理解的,因为订购背后的原因有点清楚。相比之下,如果你选择进行复杂的基于字符串的处理,通常很难理解发生了什么(特别是如果它是一个更大的程序的一部分,而不是一个很小的例子,如这里)。

制作新类型很便宜。如果您的方法的返回值不适合&#34;适合&#34;在现有类型中,只需创建一个新类型,即使这意味着1000种类型。

答案 1 :(得分:1)

有点喋喋不休,但我发现这个问题非常有趣,也许对其他人有用,还添加了一些评论来解释:

void Main()
{
    var cardDatabase = new List<string>{
        "MW1E10",          
        "MWSTX2FFI06",         
        "FWQ02",               
        "MW1Q04",              
        "MW1Q05",              
        "FWI01",               
        "MWSTX2FFA01",         
        "DNC03",               
        "MWSTX1CKC02",         
        "MWSTX2FFI03",        
        "MW1I06",  
    };


    var orderTable = new List<string>[]{
        new List<string>
        {
            "MW1",
            "FW",
            "DN",
            "MWSTX1CK",
            "MWSTX2FF"
        },

        new List<string>
        {
            "Q",
            "J",
            "C",
            "E",
            "I",
            "A"
        }
    };


    var test = cardDatabase.Select(input => {
        var r = Regex.Match(input, "^(MW1|FW|DN|MWSTX1CK|MWSTX2FF)(A|Q|J|C|E|I|A)([0-9]+)$");
        if(!r.Success) throw new Exception("Invalid data!");

        // for each input string,
        // we are going to split it into "substrings",
        // eg: MWSTX1CKC02 will be
        // [MWSTX1CK, C, 02]
        // after that, we use IndexOf on each component
        // to calculate "real" order,

        // note that thirdComponent(aka number component)
        // does not need IndexOf because it is already representing the real order,
        // we still want to convert string to integer though, because we don't like
        // "string ordering" for numbers.

        return  new 
        {
            input = input,
            firstComponent = orderTable[0].IndexOf(r.Groups[1].Value), 
            secondComponent = orderTable[1].IndexOf(r.Groups[2].Value), 
            thirdComponent = int.Parse(r.Groups[3].Value)
        };

        // and after it's done,
        // we start using LINQ OrderBy and ThenBy functions
        // to have our custom sorting.
    })
    .OrderBy(calculatedInput => calculatedInput.firstComponent)
    .ThenBy(calculatedInput => calculatedInput.secondComponent)
    .ThenBy(calculatedInput => calculatedInput.thirdComponent)
    .Select(calculatedInput => calculatedInput.input)
    .ToList();


    Console.WriteLine(test);
}

答案 2 :(得分:0)

您可以使用Array.Sort()方法。如果您的第一个参数是字符串[],那么您需要进行排序,第二个参数包含确定顺序的复杂逻辑。

答案 3 :(得分:0)

您可以使用System.Linq命名空间提供的IEnumerable.OrderBy方法。