Sturm
Sturm

Reputation: 4125

Yield item in a list

In my application there is a List<MyItem> with a getter only:

public List<MyItem> myList
{
    get
    {
         MyHost.GetItemFromID(_i1); //this may be a long operation
         MyHost.GetItemFromID(_i2);
         MyHost.GetItemFromID(_i3);
         MyHost.GetItemFromID(_i4);
         MyHost.GetItemFromID(_i5);
    }
}

This list needs sometimes to be retrieved as whole and other times only certain item has to be accessed: i.e. myList[3]. Is there a way of not building the entire list as I only need the fourth item?

Upvotes: 0

Views: 675

Answers (4)

nawfal
nawfal

Reputation: 73253

Note:

This assumes by '4th item' you mean an ordinal number, not the actual id itself.


Build a map in advance and return only items you wish to.

List<int> map = new List<int> 
{ 
    _i1,
    _i2, 
    _i3, 
    _i4, 
    _i5,     
};

public IEnumerable<MyItem> myList(params int[] indices)
{
    if (!indices.Any())
        return map.Select(MyHost.GetItemFromID);

    return indices.Select(i => MyHost.GetItemFromID(map[i]));
}

// so you call 
myList(); // for all items; decide on this API, may be separate to two methods?
myList(0); // or
myList(4); // or
myList(1, 3); // all loaded only on demand

Use a dictionary if you want more control over indexing. Ideally, for some reason this looks like you should be passing all IDs to DB in one go and SQL directly handle it, if that is the case.

Upvotes: 1

Tim Schmelter
Tim Schmelter

Reputation: 460238

You could return IEnumerable<MyItem> and use yield return:

public IEnumerable<MyItem> MyItems
{
    get
    {
        yield return MyHost.GetItemFromID(_i1); //this may be a long operation
        yield return MyHost.GetItemFromID(_i2);
        yield return MyHost.GetItemFromID(_i3);
        yield return MyHost.GetItemFromID(_i4);
        yield return MyHost.GetItemFromID(_i5);
    }
}

Then it is using deferred execution and you can write:

var fourItems = MyItems.Take(4).ToList();

Note that it might be a good idea to change the order of execution if the order doesn't matter and only the first call of GetItemFromID takes more time than the others:

yield return MyHost.GetItemFromID(_i2);
yield return MyHost.GetItemFromID(_i3);
yield return MyHost.GetItemFromID(_i4);
yield return MyHost.GetItemFromID(_i5);
yield return MyHost.GetItemFromID(_i1); //this may be a long operation

I think i have misuderstood the requirement. I've read "sometimes I only need the fourth item" as "only need four items"

So you could use my aproach but with ElementAt:

MyItem fourthItem = MyItems.ElementAt(3);

If you don't know if there's a fourth use ElementAtOrdefault.

Upvotes: 3

Matthew Watson
Matthew Watson

Reputation: 109762

I don't know what type your keys for _i1 etc are, but let's assume they are ints. Then you could do this:

public IEnumerable<MyItem> GetItems(params int[] keys)
{
    return keys.Select(key => MyHost.GetItemFromID(key));
}

Which you could call like this:

var myItems = GetItems(_i2, _i3, _i5).ToList();

or

var myItems = GetItems(_i3).ToArray();

and so on.

Note: If you only ever want a list returned, you can do the conversion inside GetItems() itself:

public List<MyItem> GetItems(params int[] keys)
{
    return keys.Select(key => MyHost.GetItemFromID(key)).ToList();
}

This approach requires a method rather than a property, though.

Upvotes: 1

barakcaf
barakcaf

Reputation: 1324

You could wrap the list with a class, say 'myListContainer' and overload its '[]' operator so that you could do something like this:

myListContainer[3] 

which will invoke the call

 MyHost.GetItemFromID(_i3);

and return the desired list item.

I'll add a full example if needed

EDIT

 public class myListContainer
{

    public MyItem this[int i]
    {
        get
        {
            return MyHost.GetItemFromID(i);
        }

    }
}

and add method to get the entire list.

Upvotes: 5

Related Questions