2015-04-26 4 views
5

У меня есть список данных без дубликатов. Для этого примера я скажу, что мой списокВыполните операцию над каждым элементом в списке по всем другим элементам в списке с исключениями

List<string> list1 = new List<string>() { "A", "B", "C", "D" }; 

Я хочу, чтобы выполнить свою работу по каждому пункту в списке против каждого другого элемента в списке, за исключением, когда я уже выполнил свою работу на них (AB и BA) или если они одинаковы (AA).

EG.

A against B 
A against C 
A against D 
B against C 
B against D 
C against D 

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

EG.

A against A - Skip 
A against B - Good 
A against C - Good 
A against D - Good 
B against A - Skip (we already did A against B) 
B against B - Skip 
B against C - Good 
B against D - Good 
C against A - Skip 

и так далее.

То, что я искал (и я даже не знаю, существует ли оно), - это простой метод, который я могу использовать для этого, вместо того, чтобы отключать два цикла и выполнять свою работу и сохранять результаты для сравнения с более поздними ,

Перебор в списке O(n*n) но мне не нужно сравнивать более половины результатов это пустая трата времени, как я знаю, что мне нужно только проверить O(n*(n/2))

код, я в настоящее время используя следующим образом

List<string> list1 = new List<string>() { "A", "B", "C", "D" }; 
List<string> list2 = new List<string>(list1); 
List<string> listResult = new List<string>(); 

list2.Reverse(); 

int i = 0; 
foreach (var a in list1) 
{ 
    for (int j = 0; j < (list2.Count/2); j++) 
    { 
     i++; 
     Console.WriteLine("Looped {0} times", i); 

     // Don't run against ourself 
     if (a == list2[j]) 
      continue; 

     if (listResult.Count(x => (x == a + list2[j]) || (x == list2[j] + a)) == 0) 
     { 
      listResult.Add(a + list2[j]); 

      // Perform some operation here 
      // operation(a, list2[j]); 
     } 
    } 
} 

Приведенный выше код работает нормально (я должен был бы регулировать часть list2.Count/2 для учета нечетным нумерованного списка).

Есть ли лучший способ сделать это? Метод расширения LINQ, который я пропустил? Моя проблема в том, что я действительно не знаю, что для Google.

Я задавался вопросом, был ли метод, который возвратил бы список, содержащий только те элементы, которые я хотел бы, чтобы затем прокрутить и выполнить свою операцию. Возможно, что-то использует .SelectMany()

+0

ли все элементы в списке уникальны? т. е. Может ли ваш список когда-либо быть «A B A C D»? –

+0

Есть ли дубликаты в 'list1'? – ASh

+0

(BTW - приведенный выше код пропускает операцию на A и B.) –

ответ

10

Для каждой записи в списке сопоставляются все записи, которые поступают после нее в списке, поскольку элементы, предшествующие этому, уже были сопоставлены.

List<string> list1 = new List<string>() { "A", "B", "C", "D" }; 

for(int i = 0; i < list1.Count - 1; i++) 
    for(int j = i + 1; j < list1.Count; j++) 
     Console.WriteLine("{0} against {1}", list1[i], list1[j]); 

Edit: Что касается вашего второго вопроса, как о чем-то вроде этого:

public static class Extensions 
{ 
    public static IEnumerable<U> Combinations<T, U>(this IEnumerable<T> list, 
                Func<T, T, U> combinator) 
    { 
     var temp = list.ToArray(); 
     for(int i = 0; i < temp.Length - 1; i++) 
      for(int j = i + 1; j < temp.Length; j++) 
       yield return combinator(temp[i], temp[j]); 
    } 
} 

, который затем может быть использован, как это:

List<string> list1 = new List<string>() { "A", "B", "C", "D" }; 
var res = list1.Combinations((a, b) => string.Format("{0} against {1}", a, b)); 

Если вы можете жить с ней поддержки просто IList вместо любого IEnumerable, вы можете полностью пропустить звонок ToArray.

+3

Мне нравится этот подход для того, чтобы он был самым чистым и быстрым. *** AND *** не использует дополнительной памяти. Это означает, что он не попадет в исключение 'Stack Overflow' с * чрезвычайно * большим' List'ом, как остальные. –

5

Просто запустите каждый элемент для всех элементов, которые идут в списке.

for (int i = 0; i < list.Count; i++) { 
    for (int j = i + 1; j < list.Count; j++) { 
     //Run list[i] against list[j] 
    } 
} 

Это гарантирует, что ни один элемент не будет запущен против него или какого-либо элемента, с которым он уже был запущен.

1

Здесь мы просматриваем все элементы в каскадных петлях foreach. Мы добавляем элемент только в том случае, если он еще не существует или оба они одинаковы, например. "AA".

List<string> list1 = new List<string>() { "A", "B", "C", "D" }; 
List<string> result = new List<string>(); 

foreach (string a in list1) 
    foreach (string b in list1) 
     if (!result.Contains(b + a) && a != b) result.Add(a + b); 
+4

Почему, по мнению людей, LINQ является универсальным ответом на каждую проблему? –

+0

@OndrejTucny Это отличный молот. – jdphenix

+0

Возврат к исходному запросу «LINQ попытка». Полностью разные решения должны размещаться как отдельные ответы, а не изменения. –

1

Мне нравится @ Крис ответ еще, если вам нужно выписать пропуск операции могут быть Ис превращаемся в:

List<string> list1 = new List<string>() { "A", "B", "C", "D" }; 
List<string> listResult = new List<string>(); 

for (int i = 0; i < list1.Count; i++) 
{ 
    for (int k = 0; k < list1.Count; k++) 
    { 
     if (k <= i) 
     { 
      Console.WriteLine("{0} against {1} - Skip", list1[i], list1[k]); 
     } 
     else 
     { 
      Console.WriteLine("{0} against {1} - Good", list1[i], list1[k]); 
      listResult.Add(list1[i] + list1[k]); 
     } 
    } 
} 
Смежные вопросы