【问题标题】:Selecting max number from a list of string in LINQ?从 LINQ 中的字符串列表中选择最大数量?
【发布时间】:2016-05-24 13:18:35
【问题描述】:

尝试从字符串列表中选择最大位数:

 int maxDigit = this.myList.Where(x=> x.Name.Any(Char.IsDigit))
                .Select(x => int.Parse(x.Name)).DefaultIfEmpty(0).Max();

int.Parse(x.Name) 导致异常,因为它返回整个名称字符串,例如'myValue99' 当然不能解析为 int。我只想返回 99。我不知道数字在字符串中的位置,因此例如不能取最后两个。

对于字符串不包含数字的情况,我需要 DefaultIfEmpty。

【问题讨论】:

标签: c# linq


【解决方案1】:

假设您想要最大 number 而不是最大 digit,您所需要的只是将"stuff99" 转换为99 的函数。那么 Linq 部分就变成小菜一碟了:

int maxNumber = myList.Max(ExtractNumberFromText);

或者,更接近您的规格:

int maxNumber = myList
         .Select(ExtractNumberFromText)
         .DefaultIfEmpty(0)
         .Max(); 

@Codecaster 已经在本网站的第二部分指出了一些适用的答案。我采用了一个简单的。没有错误检查。

// the specs: Only ever going to be my stuff1, stuff99     
int ExtractNumberFromText(string text)
{      
   Match m = Regex.Match(text, @"\d*");            
   return int.Parse(m.Groups[0].Value);       // exception for "abc"
       // int.Parse("0" + m.Groups[0].Value); // use this for default to 0
}

【讨论】:

    【解决方案2】:

    你应该只从你的字符串中选择和解析数字字符

    int maxDigit = this.myList.Where(x => x.Name.Any(Char.IsDigit))
        .Select(x => int.Parse(new string(x.Name.Where(Char.IsDigit).ToArray())))
        .DefaultIfEmpty(0).Max();
    

    【讨论】:

    • 但是现在你将每个字符串的每个字符循环两次。
    • @CodeCaster 仅适用于那些有数字的人:p
    【解决方案3】:

    假设输入可以包含以下类别:

    • nulls
    • 空字符串
    • 只有字母字符的字符串
    • 混合字母和数字字符的字符串
    • 只有数字字符的字符串

    您想引入一种方法来提取数字(如果有),或者如果没有则返回有意义的值:

    private static int? ParseStringContainingNumber(string input)
    {
        if (String.IsNullOrEmpty(input))
        {
            return null;
        }
    
        var numbersInInput = new String(input.Where(Char.IsDigit).ToArray());
        if (String.IsNullOrEmpty(numbersInInput))
        {
            return null;
        }
    
        int output;
    
        if (!Int32.TryParse(numbersInInput, out output))
        {
            return null;
        }
    
        return output;
    }
    

    请注意,并非Char.IsDigit 返回true 的所有字符都可以被Int32.Parse() 解析,因此TryParse

    然后你可以将你的列表提供给这个方法:

    var parsedInts = testData.Select(ParseStringContainingNumber)
                             .Where(i => i != null)
                             .ToList();
    

    然后对parsedInts 列表做任何你想做的事情,比如在上面调用IEnumerable<T>.Max()

    有以下测试数据:

    var testData = new List<string>
    {
        "۱‎", // Eastern Arabic one, of which Char.IsDigit returns true.
        "123",
        "abc456",
        null,
        "789xyz",
        "foo",
        "9bar9"
    };
    

    这会返回:

    123
    456
    789
    99
    

    特别注意最新的案例。

    【讨论】:

      【解决方案4】:

      查找每个字符串中的最大位数(不是数字):

      static void Main(string[] args)
      {
          List<string> strList = new List<string>() { "Value99", "46Text" };
          List<int> resultList = new List<int>();
      
          foreach (var str in strList)
          {
              char[] resultString = Regex.Match(str, @"\d+").Value.ToCharArray();
              int maxInt = resultString.Select(s => Int32.Parse(s.ToString())).Max();
              resultList.Add(maxInt);
          }
      } 
      

      【讨论】:

        【解决方案5】:

        使用正则表达式可以很简单。

        你说的是99,所以你需要跨越一位数

        var maxNumber = myTestList.SelectMany(x => getAllNumnbersFromString(x.Name)).DefaultIfEmpty(0).Max();
        
        static List<int> getAllNumnbersFromString(string str)
        {
            List<int> results = new List<int>();
        
            var matchesCollection = Regex.Matches(str, "[0-9]+");
        
            foreach (var numberMatch in matchesCollection)
            {
                results.Add(Convert.ToInt32(numberMatch.ToString()));
            }
        
            return results;
        }
        

        只检查一位数:

        int maxNumber = myTestList.SelectMany(x => x.Name.ToCharArray().ToList())
                                    .Select(x => Char.IsDigit(x) ? (int)Char.GetNumericValue(x) : 0)
                                    .DefaultIfEmpty(0).Max();
        

        【讨论】:

        • Char.GetNumericValue 返回一个双精度值,因此这将导致 maxNumber 成为 double 而不是 int
        • @juharr 这是一个很好的例子,我们不应该使用 var,感谢指出。
        【解决方案6】:

        可能有一种更巧妙的方法可以做到这一点,但我会这样做:

        int tmp = 0;
        int maxDigit = this.myList.Where(x=> x.Name.Any(Char.IsDigit))
                        .Select(x => 
        (int.TryParse(x.Name,out tmp ) ? int.Parse(x.Name) : 0 ) ).Max();
        

        您必须记住,如果 Parse 无法解析该值,它将出错,但 TryParse 只会给您错误。

        【讨论】:

        • myValue99 包含数字,但不会被解析,而 OP 想要它。
        • 同时解析字符串两次似乎是个坏主意。要么直接解析,要么使用TryParse的out参数结果。
        • 不确定在开启优化后是否会丢失“tmp”的值。认为这至少会让他得到他的结果。它并不优雅,但它会起作用。
        • @JoshK 不,因为 CodeCaster 给出的原因,它不起作用。
        • 无论出于何种原因,downvote 都是合理的。这段代码在某些方面是不正确且未优化的,显然人们发现这足以投反对票。
        猜你喜欢
        • 2023-04-04
        • 2018-12-15
        • 2016-10-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-09
        • 1970-01-01
        相关资源
        最近更新 更多