В C # .NET 2.0, что является простым способом сделать foreach в обратном порядке? - PullRequest
16 голосов
/ 17 сентября 2008

Допустим, у меня есть объект Dictionary:

Dictionary myDictionary<int, SomeObject> = new Dictionary<string, SomeObject>();

Теперь я хочу перебрать словарь в обратном порядке. Я не могу использовать простой цикл for, потому что я не знаю ключей словаря. A foreach легко:

foreach (SomeObject object in myDictionary.Values)
{
    // Do stuff to object
}

Но как я могу выполнить это в обратном порядке?

Ответы [ 14 ]

28 голосов
/ 17 сентября 2008

Словарь или любая другая форма хеш-таблицы не имеет порядка. То, что вы пытаетесь сделать, бессмысленно:)

19 голосов
/ 17 сентября 2008

Я бы использовал SortedList вместо словаря. Вы по-прежнему можете обращаться к нему по ключу, но вы также можете обращаться к нему по индексу.

SortedList sCol = new SortedList();

sCol.Add("bee", "Some extended string matching bee");
sCol.Add("ay", "value matching ay");
sCol.Add("cee", "Just a standard cee");

// Go through it backwards.
for (int i = sCol.Count - 1; i >=0 ; i--)
    Console.WriteLine("sCol[" + i.ToString() + "] = " + sCol.GetByIndex(i));

// Reference By Key
foreach (string i in sCol.Keys)
    Console.WriteLine("sCol[" + i + "] = " + sCol[i]);

// Enumerate all values
foreach (string i in sCol.Values)
    Console.WriteLine(i);

Стоит отметить, что в отсортированном списке хранятся пары ключ / значение, отсортированные только по ключу.

18 голосов
/ 17 сентября 2008

Если у вас есть .NET 3.5, вы можете использовать метод расширения .Reverse () в IEnumerables. Например:

foeach (SomeObject o in myDictionary.Values.Reverse())
{
     // Do stuff to object
}
4 голосов
/ 17 сентября 2008

На самом деле, в C # 2.0 вы можете создать свой собственный итератор, который перебирает контейнер в обратном порядке. Затем вы можете использовать этот итератор в своем выражении foreach. Но ваш итератор должен иметь способ навигации по контейнеру. Если это простой массив, он может вернуться назад так:

static IEnumerable<T> CreateReverseIterator<T>(IList<T> list)
{
    int count = list.Count;
    for (int i = count - 1; i >= 0; --i)
    {
        yield return list[i];
    }
}

Но, конечно, вы не можете сделать это с помощью словаря, так как он не реализует IList или не предоставляет индексатор. Сказать, что в Словаре нет порядка, не так: конечно, у него есть порядок. Этот порядок может быть даже полезен, если вы знаете, что это такое.

Для решения вашей проблемы: я бы сказал, скопировать элементы в массив и использовать вышеупомянутый метод, чтобы пройти его в обратном порядке. Как это:

static void Main(string[] args)
{
    Dictionary<int, string> dict = new Dictionary<int, string>();

    dict[1] = "value1";
    dict[2] = "value2";
    dict[3] = "value3";

    foreach (KeyValuePair<int, string> item in dict)
    {
        Console.WriteLine("Key : {0}, Value: {1}", new object[] { item.Key, item.Value });
    }

    string[] values = new string[dict.Values.Count];
    dict.Values.CopyTo(values, 0);

    foreach (string value in CreateReverseIterator(values))
    {
        Console.WriteLine("Value: {0}", value);
    }

}

Копирование ваших значений в массив может показаться плохой идеей, но в зависимости от типа значения это не так уж и плохо. Вы можете просто копировать ссылки!

3 голосов
/ 17 сентября 2008

Если у вас нет .NET 3.5 и, следовательно, метод обратного расширения, вы можете реализовать свой собственный. Я предполагаю, что он, вероятно, генерирует промежуточный список (при необходимости) и повторяет его в обратном порядке, что-то вроде следующего:

public static IEnumerable<T> Reverse<T>(IEnumerable<T> items)
{
    IList<T> list = items as IList<T>;
    if (list == null) list = new List<T>(items);
    for (int i = list.Count - 1; i >= 0; i-- )
    {
        yield return list[i];
    }
}
3 голосов
/ 17 сентября 2008

Я согласен с @leppie, но думаю, что вы заслуживаете ответа на вопрос в целом. Возможно, вы хотели, чтобы вопрос был в целом, но случайно выбрали неверную структуру данных. Порядок значений в словаре следует считать зависящим от реализации; согласно документации, это всегда тот же порядок, что и для ключей, но этот порядок также не указан.

В любом случае, нет простого способа заставить foreach работать в обратном порядке. Это синтаксический пример использования перечислителя класса, и перечислители могут перемещаться только в одном направлении. Технически ответом может быть «обратный сбор, затем перечисление», но я думаю, что это тот случай, когда вам просто нужно использовать цикл «назад» для:

for (int i = myCollection.Length - 1; i >= 0; i--)
{
    // do something
}
1 голос
/ 17 сентября 2008

Единственный способ, которым я могу придумать в .NET 2.0 , - это сначала скопировать все значения в список, перевернуть список и затем запустить foreach для этого списка:

Dictionary<int, object> d;
List<object> tmplist;
foreach (object o in d.Values) tmplist.Add(s);
tmplist.Reverse();
foreach (object o in tmplist) {
    //Do stuff
}
1 голос
/ 17 сентября 2008

Это будет Dictionary<int, SomeObject> myDictionary, и вы сделаете это:

foreach(SomeObject _object in myDictionary.Values.Reverse())
{
}
0 голосов
/ 01 сентября 2012
foreach (Sample in Samples)

try the following:

Int32 nEndingSample = Samples.Count - 1;

for (i = nEndingSample; i >= 0; i--)
{
     x = Samples[i].x;
     y = Samples[i].y;
}
0 голосов
/ 17 сентября 2008

Дословный ответ:

Dictionary<int, SomeObject>  myDictionary = new Dictionary<int, SomeObject>();

foreach (var pair in myDictionary.OrderByDescending(i => i.Key))
{
    //Observe pair.Key
    //Do stuff to pair.Value
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...