Rick Eyre
Rick Eyre

Reputation: 2475

IEnumerable Extension

I want to make an IEnumerable<TSource> extension that can convert itself to a IEnumerable<SelectListItem>. So far I have been trying to do it this way:

    public static 
      IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(this 
      IEnumerable<TSource> enumerable, Func<TSource, TKey> text, 
                                       Func<TSource, TKey> value)
    {
        List<SelectListItem> selectList = new List<SelectListItem>();

        foreach (TSource model in enumerable)
            selectList.Add(new SelectListItem() { Text = ?, Value = ?});

        return selectList;
    }

Is this the right way to go about doing it? If so how do I draw the values from the appropriate values from the Func<TSource, TKey> ?

Upvotes: 15

Views: 2187

Answers (7)

Dennis
Dennis

Reputation: 37780

You're re-inventing the wheel. It is what Enumerable.Select intended for.

EDIT BY @KeithS: To answer the question, if you want this output, you can define an extension method wrapping Enumerable.Select:

public static IEnumerable<SelectListItem> ToSelectItemList<TSource>(
  this IEnumerable<TSource> enumerable,
  Func<TSource, string> text,
  Func<TSource, string> value)
{ 
  return enumerable.Select(x=>new SelectListItem{Text=text(x), Value=value(x));
}

Upvotes: 26

Martin Liversage
Martin Liversage

Reputation: 106916

You just need to use the two functions you supply as parameters to extract the text and the value. Assuming both text and value are strings you don't need the TKey type parameter. And there is no need to create a list in the extension method. An iterator block using yield return is preferable and how similar extension methods in LINQ are built.

public static IEnumerable<SelectListItem> ToSelectItemList<TSource>(
  this IEnumerable<TSource> enumerable,
  Func<TSource, string> text,
  Func<TSource, string> value)
{ 
  foreach (TSource model in enumerable) 
    yield return new SelectListItem { Text = text(model), Value = value(model) };
}

You can use it like this (you need to supply the two lambdas):

var selectedItems = items.ToSelecListItem(x => ..., x => ...);

However, you could just as well use Enumerable.Select:

var selectedItems = items.Select(x => new SelectListItem { Text = ..., Value = ... });

Upvotes: 15

Franky
Franky

Reputation: 651

Other solutions work as well, but I think that the one from Martin Liversage is the best way to do it:

IEnumerable<SelectListItem> selectListItems = items.Select(x => 
    new SelectListItem 
        { 
            Text = x.TextProperty, 
            Value = x.ValueProperty 
        });

Upvotes: 0

Thinking Sites
Thinking Sites

Reputation: 3542

You're on the right path.

Funcs are methods stored in variables, and are invoked like normal methods.

public static IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(
    this IEnumerable<TSource> enumerable,
    Func<TSource, TKey> text,
    Func<TSource, TKey> value)
{
    List<SelectListItem> selectList = new List<SelectListItem>();

    foreach (TSource model in enumerable)
    {
        selectList.Add(new SelectListItem()
        {
            Text = text(model),
            Value = value(model)
        });
    }

    return selectList;
}

If I might recommend, your Funcs should be Func<TSource, string> as the text and value are strings in the SelectListItem.

Edit Just thought of this...

Also you don't have to create an inner list but can do a yield return instead. Below is my "optimized" version of your method.

public static IEnumerable<SelectListItem> ToSelectItemList<TSource>(
    this IEnumerable<TSource> enumerable,
    Func<TSource, string> text,
    Func<TSource, string> value)
{
    foreach (TSource model in enumerable)
    {
        yield return new SelectListItem()
        {
            Text = text(model),
            Value = value(model)
        };
    }
}

Here is the reference for yeild return. It allows you to return the your results as an element in an enumerable, constructing your enumerable invisibly (to you).

http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

Upvotes: 15

Patrick McDonald
Patrick McDonald

Reputation: 65451

A LINQ way of achieving what you want would be:

public static IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(
    this IEnumerable<TSource> enumerable, 
    Func<TSource, TKey> textSelector, 
    Func<TSource, TKey> valueSelector)
{
    return from model in enumerable
           select new SelectListItem 
           { 
               Text = textSelector(model), 
               Value = valueSelector(model) 
           };
}

Upvotes: 4

Rune FS
Rune FS

Reputation: 21752

To me it seems like crossing a river to get water. Why not simply use select?

enumerable.Select(item => 
                    new SelectListItem{
                          Text = item.SomeProperty, 
                           Value item.SomeOtherProperty
                    }).ToList();

if you really do want a method then you could do this:

public static 
      IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(this 
      IEnumerable<TSource> enumerable, Func<TSource, TKey> text, 
                                       Func<TSource, TKey> value)
    {
        return (from item in enumerable
                select new SelectListItem{
                      Text = text(item),
                      Value = value(item)
                }).ToList();  
    }

Upvotes: 4

Michael Edenfield
Michael Edenfield

Reputation: 28338

Within the body of your extension method, those two parameters are just delegates, and you can run them like any other function:

        selectList.Add(new SelectListItem() { Text = text(model), Value = value(model)});

Upvotes: 0

Related Questions