Hardgraf
Hardgraf

Reputation: 2626

Selecting max number from a list of string in LINQ?

trying to select the max digit from a list of strings:

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

It is int.Parse(x.Name) which is causing an exception as this is returning the entire name string e.g. 'myValue99' which of course cannot be parsed to an int. I just want to return 99. I don't know where the digits will be in the string therefore cannot for example take the last two.

I need the DefaultIfEmpty for cases where the string does not contain a number.

Upvotes: 0

Views: 3965

Answers (6)

CodeCaster
CodeCaster

Reputation: 151584

Assuming the input can contain the following categories:

  • nulls
  • Empty strings
  • Strings with only alphabetical characters
  • Strings with mixed alphabetical and numerical characters
  • Strings with only numerical characters

You want to introduce a method that extracts the number, if any, or returns a meaningful value if not:

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;
}

Note that not all characters for which Char.IsDigit returns true can be parsed by Int32.Parse(), hence the TryParse.

Then you can feed your list to this method:

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

And do whatever you want with the parsedInts list, like calling IEnumerable<T>.Max() on it.

With the following test data:

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

This returns:

123
456
789
99

Especially note the latest case.

Upvotes: 1

Henk Holterman
Henk Holterman

Reputation: 273219

Assuming you want the max number and not the max digit, all you need is a function to convert "stuff99" to 99. Then the Linq part becomes child's play:

int maxNumber = myList.Max(ExtractNumberFromText);

or, to be closer to your specs:

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

@Codecaster already pointed to a few applicable answers on this site for the second part. I adopted a simple one. No error checking.

// 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
}

Upvotes: 4

Zein Makki
Zein Makki

Reputation: 30022

It can be simple using Regex.

You stated 99, so you need to span more than one digit:

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;
}

One digit only check:

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

Upvotes: 0

poppertech
poppertech

Reputation: 1294

To find the max digit (not number) in each string:

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);
    }
} 

Upvotes: 0

JoshK
JoshK

Reputation: 347

Probably there's a slicker way to do this but I would just do:

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();

You have to remember that Parse will error out if it can't parse the value but TryParse will just give you false.

Upvotes: -1

fubo
fubo

Reputation: 45947

you should only select and parse the Digit characters out of your string

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();

Upvotes: 3

Related Questions