8
\$\begingroup\$

I have a method that exacts data from an XML and returns an IEnumerable<Foo>. I am trying to create a method that will merge the results from 2 different XML's files into a single IEnumerable and remove any duplicate data that may be found

A duplicate in this case is defined as 2 Foo objects that have the same Name property even if the other data is different. If there is a duplicate, I always want to keep the Foo from the first IEnumerable and discard the 2nd.

Am I on the right track with this LINQ query, or is there a better way to accomplish what I am trying to do?

public IEnumerable<Foo> MergeElements(XElement element1, XElement element2)
{
    IEnumerable<Foo> firstFoos = GetXmlData(element1); // get and parse first set
    IEnumerable<Foo> secondFoos = GetXmlData(element2); // get and parse second set

    var result = firstFoos.Concat(secondFoos)
                          .GroupBy(foo => foo.Name)
                          .Select(grp => grp.First());

    return result;
}

The biggest concern I have with this code is that I am not certain the duplicate filtering rules I want will be guaranteed. I know Concat will just append secondFoos to the end of firstFoos, but when calling GroupBy(), will the resulting IGrouping object always have elements in the same order as the source data?

\$\endgroup\$
2
  • \$\begingroup\$ Can you implement Equals() (and preferably also IEquatable<Foo>) on Foo, to compare by Name? \$\endgroup\$
    – svick
    Apr 23, 2012 at 17:34
  • \$\begingroup\$ @svick Foo already does that through overriding Equals() (although it does not implement IEquatable, but I can add it). I thought of using Distinct(), but I've read it is unordered, so I was concerned my filtering rules would not be guaranteed. \$\endgroup\$ Apr 23, 2012 at 17:40

1 Answer 1

7
\$\begingroup\$

With things like this, it's usually best to look at the documentation.

Is your approach with GroupBy() going to work? Yes:

Elements in a grouping are yielded in the order they appear in source.

Can you use Distinct() to do the same thing more concisely? No (at least you shouldn't depend on it):

The Distinct method returns an unordered sequence that contains no duplicate values.

Is there some other way? Yes, you can use Union():

When the object returned by this method is enumerated, Union enumerates first and second in that order and yields each element that has not already been yielded.

So, I think the best way to do it is:

var result = firstFoos.Union(secondFoos);
\$\endgroup\$
1
  • \$\begingroup\$ Great answer. I was pretty sure Distinct() was not my solution, but somehow missed Union() in the documentation. \$\endgroup\$ Apr 23, 2012 at 18:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.