2013-08-23 2 views
0

mı я сравнивая элементы ListView с Generic элементов списка с List.Any метод как это:Список <T> .Any(); Как получить индекс совпадающего элемента?

foreach (ListViewItem itemRow in lstviewAddsheets.Items) 
{ 
    if (InvalidSheets.Any(x => x != null && x.FilePath == itemRow.Tag.ToString())) 
      { 
      //Math found 
      } 
} 

Скажите, пожалуйста, как получить индекс списка InvalidSheets который был согласован с itemRow.Tag.ToString().

ответ

4

Поскольку кажется некоторые дебаты о том, насколько быстрее он будет использовать List.FindIndex() вместо Linq, чтобы найти индекс, я написал тестовую программу.

Это предполагает, что вам нужно только найти индекс первого совпадающего элемента в списке. Он не обрабатывает несколько совпадающих элементов.

Также обратите внимание, что этот тест является наихудшим, поскольку соответствующий элемент находится в самом конце списка.

Мои результаты для построения релиза x86 (работают на Windows 8 x64, четырехъядерный процессор):

Calling Via FindIndex() 100 times took 00:00:00.9326057 
Calling Via Linq 100 times took 00:00:04.0014677 
Calling Via FindIndex() 100 times took 00:00:00.8994282 
Calling Via Linq 100 times took 00:00:03.9179414 
Calling Via FindIndex() 100 times took 00:00:00.8971618 
Calling Via Linq 100 times took 00:00:03.9134804 
Calling Via FindIndex() 100 times took 00:00:00.8963758 

, показывающие, что List.FindIndex() примерно в четыре раза быстрее, чем при использовании Linq.

Вот код теста:

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 

namespace Demo 
{ 
    class Test 
    { 
     public string FilePath; 
    } 

    class Program 
    { 
     private void run() 
     { 
      int count = 1000000; 

      List<Test> list = new List<Test>(count); 

      for (int i = 0; i < count; ++i) 
       list.Add(new Test{ FilePath = i.ToString()}); 

      string target = (count-1).ToString(); 

      for (int trial = 0; trial < 4; ++trial) 
      { 
       Action viaFindIndex = 
       (
        () => 
        { 
         int index = list.FindIndex(x => (x != null) && (x.FilePath == target)); 
        } 
       ); 

       Action viaLinq = 
       (
        () => 
        { 
         int index = list.Select((x, i) => new { Item = x, Index = i }) 
         .First(x => (x != null) && (x.Item.FilePath == target)) 
         .Index; 
        } 
       ); 

       viaFindIndex.TimeThis("Via FindIndex()", 100); 
       viaLinq.TimeThis("Via Linq", 100); 
      } 
     } 

     private static void Main() 
     { 
      new Program().run(); 
     } 
    } 

    static class DemoUtil 
    { 
     public static void TimeThis(this Action action, string title, int count = 1) 
     { 
      var sw = Stopwatch.StartNew(); 

      for (int i = 0; i < count; ++i) 
       action(); 

      Console.WriteLine("Calling {0} {1} times took {2}", title, count, sw.Elapsed); 
     } 
    } 
} 

Поэтому, учитывая, что List.FindIndex() одновременно гораздо быстрее и гораздо легче читать, чем с помощью Linq, я не вижу причин, чтобы использовать Linq для решения этой конкретной проблемы.

int index = list.FindIndex(x => (x != null) && (x.FilePath == target)); 

против

int index = list.Select((x, i) => new { Item = x, Index = i }) 
      .First(x => (x != null) && (x.Item.FilePath == target)) 
      .Index; 

Первая версия выигрывает по всем пунктам ИМО.

+1

Я предполагаю, что одна из причин анонимного типа. В вашем тестовом примере вам нужно создать 1000000-1 экземпляров, тогда как «FindIndex» ему не нужен. Однако +1 для теста. –

+0

Да, это похоже на вероятный кандидат. –

+0

list.FindIndex работал отлично, быстро, просто и чисто. Спасибо. – Zeeshanef

1

Попробуйте это:

InvalidSheets.IndexOf(InvalidSheets.First(x => x != null && x.FilePath == itemRow.Tag.ToString())) 

Он получит индекс первого некорректного листа согласования сказуемого

+0

+1 Да. Если у вас есть 'List <>', то 'List.IndexOf()' намного эффективнее, чем использование Linq. –

+0

@MatthewWatson: 'IndexOf' принимает' T' не лямбду.'T' также должен переопределить' Equals'. Это также не «намного более эффективно», но в большинстве случаев приближается к микро-оптимизации. –

+0

@TimSchmelter Моя ошибка: я хотел прокомментировать метод FindIndex(). –

3

Вот как вы можете получить индекс:

var index = InvalidSheets.Select((x, i) => new {Item = x, Index = i}) 
         .First(x => x.Item != null && x.Item.FilePath == itemRow.Tag.ToString()) 
         .Index; 

Однако вы можете захотеть refactor это с FirstOrDefault вот так:

foreach (ListViewItem itemRow in lstviewAddsheets.Items) 
{ 
    var sheet = InvalidSheets.Select((x, i) => new {Item = x, Index = i}) 
          .FirstOrDefault(x => x.Item != null && x.Item.FilePath == itemRow.Tag.ToString()); 
    if (sheet != null) 
    { 
     var index = sheet.Index; 
    } 
} 
+0

Очень хороший и очень быстрый код. Спасибо, что привел меня на правильный путь. – Zeeshanef

5

Вы можете сделать это

int index = InvalidSheets.FindIndex(x => x != null && x.FilePath == itemRow.Tag.ToString()); 

, если вы хотите, чтобы получить объект непосредственно, то сделать это

var matchedObject = InvalidSheets.FirstOrDefault(x => x != null && x.FilePath == itemRow.Tag.ToString()); 
0

Вы можете проецировать индекс с перегрузкой, поэтому вам необходимо выбрать анонимный тип:

var invalids = InvalidSheets.Select((s, i) => { Sheet=s, Index=i }) 
    .Where(x => x.Sheet != null && x.Sheet.FilePath == itemRow.Tag.ToString())); 
bool anyInvalid = invalids.Any(); // is any invalid 
IEnumerable<int> indices = invalids.Select(x => x.Index);// if you need all indices