Search code examples
c#dictionaryyieldconsumerproducer

Producing Single Item and Moving Counter Forward


I'm working on a problem where I need to provide one item at a time to the consumer and move the counter forward so next item and so on until ran out of items. I came up with first draft of code (see below). The underlying data structure contains a Dictionary which contains string as its key and hold another dictionary as its value, which holds object of type Entity.

I've a sense that I do need to maintain the state somehow, so I tried using yield return statement but not sure how to jell it together. Also I think using forearch/iterator may need adjustment since consumer will call GetNextItem() until it returns false(meaning run out of items).

private static Dictionary<string, Dictionary <uint,Entity>> dt;   
private uint localCounter=0 , globalCounter = 0;

public Entity GetNextItem()
{
    foreach (string key in dt.Keys )
    {
        if (MoveCounters(key)) //counter is moved, so process the next item
        {
            yield return dt[key][localCounter];
        }
    }

}

private bool MoveCounters(string key)
{
    if (++globalCounter > dt.Count) return false; //hit the limit
    if (++localCounter >  dt[key].Count)
    {
        globalCounter++;
        return true;
    }

    localCounter++;
    return true;
   }
}


public class Entity
{
    Dictionary<string, string> dtValues; //contains values from CSV file.
}

Solution

  • You're failing to reset localCounter to zero when you move to the next sublist.

    That said, you can do this so much easier:

    foreach (var subdt in dt.Values)
       foreach (var item in subdt.Values)
           yield return item;
    

    But it's even easier using LINQ SelectMany

    return dt.Values.SelectMany(subdt => subdt.Values);
    

    Note that the last one does not use yield return, since LINQ produces the enumerable and you just return it.

    If you want to log the keys and counters as well, try something like:

    int iList = 0;
    foreach( var subdt in dt ) {
        /* log iList, subdt.Key */
        int iItem = 0;
        foreach( var item in subdt.Value ) {
           /* log iItem, item.Key */
           ++iItem;
           yield return item.Value;
        }
        ++iList;
    }