1

Is there an elegant way to build an IEnumerable<T> from

bool TryParse<T>(Input, out T) 

and an input of type

Input[,]

Basically I have a 2D array of Input and would like to call TryParse on each Input, if TryParse<T> returns true I will append the return value in 'out' to an IEnumerable<T>.

I can do this easily in a for loop but I wanted something more elegant. This is what I use at the moment:

        var marketInputColl = new Collection<MarketInput>();
        foreach (object o in marketInputs)
        {
            MarketInput mktInput;
            if (ExcelCache.TryGetCache<MarketInput>(o.ToString(), out mktInput))
                marketInputColl.Add(mktInput);
        }
BlueTrin
  • 9,610
  • 12
  • 49
  • 78

3 Answers3

2

out/ref parameters don't exactly play all that well with LINQ. You can do it, but it's messy. The preferable option is to use a parsing tool that will return a nullable int (with null if the value cannot be parsed) instead of using out:

public static int? TryParse(string s)
{
    int output;
    if (int.TryParse(s, out output))
        return output;
    else
        return null;
}

You can make a comparable function for your attempts to fetch the value from the cache, so long as you don't also need to store actual null values.

This allows you to write:

var query = data.Select(item => TryGetCache(item.ToString()))
    .Where(n => n != null);
Servy
  • 202,030
  • 26
  • 332
  • 449
2

You can do it in a generic way:

static class Extensions
{
    public delegate bool TryParseDelegate<TSource>(string s, out TSource source);

    public static IEnumerable<TResult> WhereParsed<TSource, TResult>(
                                               this IEnumerable<TSource> source,
                                               TryParseDelegate<TResult> tryParse)
    {
        // check arguments against null first

        foreach (var item in source)
        {
            TResult result;
            if (tryParse(item.ToString(), out result))
            {
                yield return result;
            }
        }
    } 
}

Usage:

var result = marketInputs.Cast<object>()
                         .WhereParsed<object, MarketInput> 
                                     // need to specify explicit,
                                    // out param type cannot be inferred from usage
                              (ExcelCache.TryGetCache).ToList();
1

The problem with TryParse() is that it's not an implementation of any interface: you'll either need to use reflection to find the method, or (simpler), just provide a conversion delegate.

Then you can do something like this, with a 2D array (or actually, arrays of any number of dimensions:

string[,] raw = { { "1" , "2" , } ,
                  { "3" , "X" , } ,
                  { "5" , "6" , } ,
                } ;
int?[] converted = raw.Cast<string>()
                   .Select( s => {
                     int value ;
                     bool parsed = int.TryParse( s , out value ) ;
                     return parsed ? (int?) value : (int?)null ;
                   })
                   .ToArray()
                   ;

If your array is jagged, you'll need one more step:

string[][] raw = { new string[]{"1","2",} ,
                   new string[]{"3","X",} ,
                   new string[]{"5","6",} ,
                 } ;
int?[] converted = raw.Cast<string[]>()
                   .SelectMany( s => s )
                   .Select( s => {
                     int  value ;
                     bool parsed = int.TryParse( s , out value ) ;
                     return parsed ? (int?) value : (int?)null ;
                   })
                   .ToArray()
                   ;

Given your example:

var marketInputColl = new Collection<MarketInput>();

foreach (object o in marketInputs)
{
  MarketInput mktInput;
  if (ExcelCache.TryGetCache<MarketInput>(o.ToString(), out mktInput))
    marketInputColl.Add(mktInput);
}

We can take the same basic approach:

Collection<MarketInput> collection = new Collection<MarketInput>(
  marketInputs
  .Cast<object>()
  .Select( o => o.ToString() )
  .Select( s => {
    MarketInput v ;
    bool parsed = ExcelCache.TryGetCache<MarketInput>( s , out v ) ;
    return parsed ? v : null ;
  })
  .Where( x => x != null )
  .ToList()
  ) ;
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135