我有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,但我现在并不是那么好,而且我自己也无法解决这个问题。我需要一本字典,正则表达式或带有正则表达式的字典吗?什么是最好的方法?
答案 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方法。