2

My class object has a dictionary in it like this:

class MyClass
{
  Dictionary<string, string> Things {get; set;}
}

now I want to get the first thing to do something special on it and then if it had more members take the rest of them and do some other thing on the rest of them

I had seen some syntax for Select statement that we could pass an "index" to Select, I was trying to figure it out but no luck. How would you do that?

All I wrote was something like this:

var temp = myClassObject.Things.Select((t,i) => t.Vale);

I think I can write it in a lengthier way by using .First and Skip(1).

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • 7
    Dictionaries are not ordered, so trying to get the "First" item could give you inconsistent results. – D Stanley Aug 11 '14 at 21:29
  • 1
    Please show your code (or pseudocode) as you would write it with `First` and `Skip(1)`. That will help get an idea of what you're trying to accomplish. – p.s.w.g Aug 11 '14 at 21:30
  • Maybe if you explain what these things are a little more, someone could come up with a suggestion of how to compose this. – Travis J Aug 11 '14 at 21:30
  • You can't. Refer my recent [answer here](http://stackoverflow.com/questions/25177836/is-it-possible-to-access-expandoobject-properties-by-index/25178233#25178233) for more info – Sriram Sakthivel Aug 11 '14 at 21:30
  • @DStanley : I am reading the rows of a file, parsing them, adding them sometimes as necessary to this dictionary, so here first means whatever row in the file first got added. – ConfusedSleepyDeveloper Aug 11 '14 at 21:31
  • @DStanley I can imagine a useful scenario where you might want to do X for the first item, but you don't really care what that item is (e.g. allocating a resource for further processing). However, in that case, I'd probably just use `Count` instead. – p.s.w.g Aug 11 '14 at 21:32
  • 2
    @ConfusedSleepyDeveloper But the first thing you add to dictionary doesn't have to be the first one you get from it. – L.B Aug 11 '14 at 21:32
  • @ConfusedSleepyDeveloper then a dictionary is not the right data structure. Dictionaries are designed to _quickly_ get items based on some key value - they to not keep track of when items were added. – D Stanley Aug 11 '14 at 21:33
  • @ConfusedSleepyDeveloper what is the key value in your dictionary? can the "order" of items be inferred from that somehow? – D Stanley Aug 11 '14 at 21:38
  • i still dont understand completely what you want but it may seem you could implement a collection other then dictionary like a Queue where T could be a KeyValuePair. – terrybozzio Aug 11 '14 at 21:43

4 Answers4

3

Dictionaries should not be used if you have to rely on a specific order. They are used to lookup a value by a key fast. However, as long as you don't remove entries the current implementation preserves the insertion order.

So you can treat it like a collection and use Take(or First if you want to stop the query) and Skip:

var firstThing   = Things.Take(1);  // or First()
var restOfThings = Things.Skip(1);
Community
  • 1
  • 1
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • yes, I am just adding to them row by row from the source text file. So I am hoping it doesn't magically change the order of the item I add to it tho! – ConfusedSleepyDeveloper Aug 11 '14 at 21:35
  • oh oh oh! didn't know that one! How about a List ? does that change the order too? – ConfusedSleepyDeveloper Aug 11 '14 at 21:38
  • @ConfusedSleepyDeveloper not if you use `Add` - it will add them to the end of the list. – D Stanley Aug 11 '14 at 21:39
  • @ConfusedSleepyDeveloper Nope, `List` maintains the order. Item will always stay where you added it. – Sriram Sakthivel Aug 11 '14 at 21:39
  • 2
    @DStanley: it adds them in a location which is based on the insertion order, at least in the current implementation. http://stackoverflow.com/a/6384765/284240 However, since it's not documented you should not rely on this implementation. It might change in future (although very unlikely). – Tim Schmelter Aug 11 '14 at 21:40
  • @TimSchmelter I stand corrected. Thanks for the link. – D Stanley Aug 11 '14 at 21:43
  • @TimSchmelter So if I use List.Add(str), this one keeps the order? – ConfusedSleepyDeveloper Aug 11 '14 at 21:48
  • 1
    @ConfusedSleepyDeveloper: yes, a `List` is an ordered collection (same as array) which is made for this since it can be accessed via index. But it's not as fast if you search a specific item. – Tim Schmelter Aug 11 '14 at 21:50
  • @TimSchmelter Sorry for taking your time so much, Last question: "Take(or First if you want to stop the query)" .. so if it most cases this list has one member, it is more efficient to use First() and then use Take/Skip if its Count() is bigger than One? – ConfusedSleepyDeveloper Aug 11 '14 at 21:53
  • 1
    @ConfusedSleepyDeveloper: i use `First` if i don't want more than the first. It'll stop deferred execution and returns the first item(or key-value-pair). `Take` on the other hand is still a _query_ which is not executed and which i can chain with other queries (or filters). It returns an `IEnumerable` even if it's actually a single item. So it depends on the requirement. I don't know what you want to do with the first and the rest, use `Take(1)` if you want to apply the same logic as to the rest even if it contains only one. Use `First` if you want the result immediatly. – Tim Schmelter Aug 11 '14 at 22:00
2

As I said in my comment, Dictionaries are not ordered, so getting the "First" item may give you inconsistent results.

That said, it does implement IEnumerable<KeyValuePair>, so you could do:

string firstItem = myClassObject.Things.First().Value;
IEnumerable<string> theRest = myClassObject.Things.Skip(1).Select(kvp => kvp.Value);

If you want to keep the items in the order in which they're added then a List<string> is more appropriate (or a List<Tuple<string, string>> if you want to keep some string value associated with the line). The main difference is that getting items will be slower (how much slower depends on the amount of data)

D Stanley
  • 149,601
  • 11
  • 178
  • 240
2

I would suggest introducing a tuple to this

Dictionary<string, Tuple<string,int>> Things {get; set;}

And then when building your data, as opposed to just using

Things["Hello"] = "World";

You would use a counter which started at 0

Things["Hello"] = new Tuple<string,int>("World",counter++);

And this would allow you to later look at the first line

var first = Things.OrderBy(k => k.Value.Item2).First();

and

var rest = Things.OrderBy(k => k.Value.Item2).Skip(1);
Travis J
  • 81,153
  • 41
  • 202
  • 273
0

As the other answers have stated, items in dictionaries not have a defined order, and you shouldn't rely on a dictionary to return the items in the same order in which they were added. If this is really something you need, I'd strongly recommend you rethink your choice of data structure.

However, since you said you didn't want to use First or Skip, you could use the raw IEnumerator instead:

using (var enumerator = myClassObject.Things.GetEnumerator())
{
    if (enumerator.MoveNext())
    {
        var firstPair = enumerator.Current;
        // Handle first pair

        while (enumerator.MoveNext())
        {
            var pair = enumerator.Current;
            // Handle subsequent pairs
        }
    }
}

To be honest, I probably wouldn't do this in my own code; I'm just offering an alternative.

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331