5

I have two List<string> and I am using the SequenceEqual method to determine if they match.

I now have a need to get a List<int>, string, IEnumerable, whatever of the indexes of the elements that have failed.

Is there an easy way?

Thanks

Jon
  • 38,814
  • 81
  • 233
  • 382
  • don't simple loops match your fit? – Andrey Atapin Dec 01 '11 at 13:21
  • What do you want 'the indexes of the elements that have failed' to mean? `SequenceEqual` returns a `bool` for "these sequences are identical in their elements". There are many different ways of *characterising* a difference - what do you want? – AakashM Dec 01 '11 at 13:50

6 Answers6

3

I think you want:

List<string> list1 = ...
List<string> list2 = ...

var differentIndices = list1.Zip(list2, (item1, item2) => item1 == item2)
                            .Select((match, index) => new { Match = match, Index = index })
                            .Where(a => !a.Match)
                            .Select(a => a.Index);

Note that if one of the lists is longer than the other, this will not consider the items beyond the length of the smaller list.

Ani
  • 111,048
  • 26
  • 262
  • 307
  • Cool, I didn't know about the `Zip` method. Looks useful for things like this. Is there a similar method that includes all items from both sequences? Seems incomplete without it. – Scott Rippey Dec 02 '11 at 00:15
  • @Scott: So beyond the length of the smaller list, you want to include all the indices of the bigger list? – Ani Dec 02 '11 at 01:31
  • 1
    No, I'm just curious if there's a method that does the same as `Zip` but extends to the length of the larger sequence instead of the smaller sequence. It could pass in `default (T)` or something. But it doesn't sound like it already exists. – Scott Rippey Dec 02 '11 at 05:46
  • @Scott: No, there isn't in standard LINQ to Objects as of .NET 4. You could write one yourself, however. – Ani Dec 02 '11 at 06:43
  • It just seems really weird to me that`Zip` exists! It really seems like a specific, one-off solution. And the name Zip is nearly meaningless. It just doesn't fit with the rest of LINQ. Just my two cents. – Scott Rippey Dec 02 '11 at 07:28
  • 1
    @Scott: Any reason you say that? `Zip` is a term from functional- programming, I believe. And the truncation to the length of the shorter sequence is consistent with the operator's implementation in other languages, such as Python. – Ani Dec 04 '11 at 17:58
  • Well that makes more sense. I have no experience with Python or functional programming, so the term is unfamiliar. Other LINQ extension methods, however, come from more familiar realms, such as SQL. – Scott Rippey Dec 05 '11 at 19:29
2

You can use LINQ:

var firstFiltered = firstList.Except(secondList);
var secondFiltered = secondList.Except(firstList);

var bothFiltered = firstFiltered.Concat(secondFiltered);

Note: I'm sure there is a more efficient way of doing this...

myermian
  • 31,823
  • 24
  • 123
  • 215
2

It sounds like you want the opposite of the "intersection". The intersection is the items that exist in both lists ... and you want the items that don't exist in both lists.
Here's a easy one-liner to do that:

var items = first.Union(second).Except(first.Intersect(second));

Example:

var first = new[]{"A","B","C","D"};
var second = new[]{"C","D","E","F"};

var items = first.Union(second).Except(first.Intersect(second));
// Result: "A","B", "E","F"
Scott Rippey
  • 15,614
  • 5
  • 70
  • 85
1

Lets say your lists are list1 and list2

 IEnumerable<string> setDifferent =
          list1.Except(list2);

The above will give you all the elements in list1 that are not in list2

parapura rajkumar
  • 24,045
  • 1
  • 55
  • 85
0

use that.

var diff = l1.Except(l2);  
Array.ForEach(diff.ToArray(), r => Console.WriteLine(r));
Turbot
  • 5,095
  • 1
  • 22
  • 30
0

If the lists can be different lengths, you could do something like this:

IList<string> first = new List<string> { "A", "B", "C", "D", "E", "F" };
IList<string> second = new List<string> { "A", "Z", "C", "D", "Y" };

IList<string> longer = first;
IList<string> shorter = second;

IEnumerable<int> differentIndices = longer.Select((s, i) => i > shorter.Count - 1 || !s.Equals(shorter[i]) ? i : -1).Where(n => n >= 0);

Console.WriteLine(string.Join(", ", differentIndices));

This is effectively a one-liner. The results for this example are "1, 4, 5".

The key point is that the Select maps the matching indices to -1, and these are then filtered out by the Where.

Matthew Strawbridge
  • 19,940
  • 10
  • 72
  • 93