0

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.

Hardgraf
  • 2,566
  • 4
  • 44
  • 77
  • 4
    What if the string is "55 stuff 66", what should the parsed `int` be in that case? – juharr May 24 '16 at 13:22
  • Good point but in this instance I don't have to worry about that. Only ever going to be my stuff1, stuff99 etc. – Hardgraf May 24 '16 at 13:23
  • 4
    The list and Linq parts are irrelevant. The same rules apply to a single string. See for example [Find and extract a number from a string](http://stackoverflow.com/questions/4734116/find-and-extract-a-number-from-a-string), [Split numeric part from alphanumeric string using C#](http://stackoverflow.com/questions/19288312/split-numeric-part-from-alphanumeric-string-using-c-sharp), [Extract numbers from string to create digit only string](http://stackoverflow.com/questions/11002527/extract-numbers-from-string-to-create-digit-only-string) and so on. – CodeCaster May 24 '16 at 13:24
  • do you want `"9 and 9"` to become `99` or `9` or maybe even `18` or `81`? – Jodrell May 24 '16 at 13:51

6 Answers6

4

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
}
H H
  • 263,252
  • 30
  • 330
  • 514
3

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();
fubo
  • 44,811
  • 17
  • 103
  • 137
1

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.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
0

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();
Zein Makki
  • 29,485
  • 6
  • 52
  • 63
  • `Char.GetNumericValue` returns a double, so that would result in `maxNumber` being a `double` instead of an `int`. – juharr May 24 '16 at 13:43
  • @juharr This is a good example where we should not use var, thanks to point that out. – Zein Makki May 24 '16 at 13:48
0

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);
    }
} 
poppertech
  • 1,286
  • 2
  • 9
  • 17
-1

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.

JoshK
  • 337
  • 4
  • 16
  • 1
    `myValue99` contains digits, but won't be parsed, while the OP wants it to. – CodeCaster May 24 '16 at 13:28
  • 1
    Also parsing the string twice seems like a bad idea. Either just parse it or use the out parameter results of `TryParse`. – juharr May 24 '16 at 13:29
  • Wasn't sure if, with optimization turned on, it might loose the value for "tmp". Figured this would at least get him his result. It's not elegant, but it would work. – JoshK May 24 '16 at 13:30
  • @JoshK No it wouldn't work for the reason CodeCaster gave. – juharr May 24 '16 at 13:31
  • As far as being a mixed alpha / numeric string, I think you have to assume that op is fine with whatever the behaviour for Parse would be on that string. Otherwise he would have had logic to strip out the non-numeric values. – JoshK May 24 '16 at 13:32
  • @JoshK I think the point is that the OP didn't know how to strip out the non-numeric characters before parsing. – juharr May 24 '16 at 13:33
  • Oh, maybe, I thought he wanted to take the parse behaviour as-is but was stuck with Parse throwing an exception for the unparseable cases. My snippet above was to get him past that without changing what he was doing too much. I think the downvotes are underved - I was trying to give him what it looked like he wanted. – JoshK May 24 '16 at 13:35