问题
I just asked another question here and the answer was spot on.
But that addressed what was essentially a syntax problem. Now I need help with an actual resolution.
This is the same code from the previou question (fixed up and with stuff added).
XElement FILE1 = XElement.Load (@"..\FILE1.XML");
XElement FILE2 = XElement.Load (@"..\FILE2.XML");
var orders = from file1 in FILE1.Descendants("Players").Elements("Player")
select new {
name=new {
clientID=ulm.Element("ClientID").Value,
firstName=file1.Element("FirstName").Value,
lastName=file1.Element("LastName").Value
}
};
var orders2 =
from file2 in FILE2.Descendants("Players").Elements("Player")
select new {
name=new {
clientID=ulm.Element("ClientID").Value,
firstName=file2.Element("FirstName").Value,
lastName=file2.Element("LastName").Value
}
};
var matchingResults = from i in orders from j in orders2 where (i.name.firstName==j.name.firstName && i.name.lastName==j.name.lastName)
select i;
matchingResults.Dump()
To make it interesting I have added a ClientID to each sequence result before trying to match them up.
What I need is to know does a Player node from order EXISTS in a player node from orders2. Or does it NOT EXIST? Ideally I would also be able to CHOOSE the selection criteria for the NOT EXISTS/EXISTS check. (LastName, or FirstName && LastName, or ClientID only, etc.)
I have NO IDEA how to go about this. Thanks for your help.
回答1:
- Use Enumerable.Intersect or Enumerable.Except
- Use concrete types instead of anonymous types.
To be able to choose the selection criteria you can create a parameterized
IEqualityComparer<Client>
.class Client { public string ClientID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } [Flags] enum Criteria { ClientID, FirstName, LastName } class ClientEqualityComparer : IEqualityComparer<Client> { private Criteria criteria; public ClientEqualityComparer(Criteria criteria) { this.criteria = criteria; } #region IEqualityComparer<Client> Membres public bool Equals(Client x, Client y) { if (criteria.HasFlag(Criteria.ClientID) && x.ClientID != y.ClientID) return false; if (criteria.HasFlag(Criteria.FirstName) && x.FirstName != y.FirstName) return false; if (criteria.HasFlag(Criteria.LastName) && x.LastName != y.LastName) return false; return true; } public int GetHashCode(Client obj) { int hash = 17; if (criteria.HasFlag(Criteria.ClientID)) hash = hash * 31 + obj.ClientID; if (criteria.HasFlag(Criteria.FirstName)) hash = hash * 31 + obj.FirstName; if (criteria.HasFlag(Criteria.LastName)) hash = hash * 31 + obj.LastName; } #endregion } static void Main(string[] args) { IEnumerable<Client> orders; IEnumerable<Client> orders2; //... var matchingIdFn = orders.Intersect(orders2, new ClientEqualityComparer(Criteria.ClientID | Criteria.FirstName)); var matchingIdFnLn = orders.Intersect(orders2, new ClientEqualityComparer(Criteria.ClientID | Criteria.FirstName | Criteria.LastName)); var different = orders.Except(orders2, new ClientEqualityComparer(Criteria.ClientID | Criteria.FirstName)); }
var orders = from file1 in FILE1.Descendants("Players").Elements("Player")
select new Client{
ClientID=ulm.Element("ClientID").Value,
FirstName=file1.Element("FirstName").Value,
LastName=file1.Element("LastName").Value
};
回答2:
I second Ahmed KRAIEM's suggestion to use Intersect
or Except
Here's another solution that lets you compare using any arbitrary lambda:
void Main()
{
XElement FILE1 = XElement.Parse(
@"<Root>
<Players>
<Player><ClientId>1</ClientId><FirstName>Bob</FirstName><LastName>Smith</LastName></Player>
<Player><ClientId>2</ClientId><FirstName>John</FirstName><LastName>Smith</LastName></Player>
</Players>
</Root>");
XElement FILE2 = XElement.Parse(
@"<Root>
<Players>
<Player><ClientId>2</ClientId><FirstName>Bob</FirstName><LastName>Smith</LastName></Player>
<Player><ClientId>3</ClientId><FirstName>Mike</FirstName><LastName>Smith</LastName></Player>
</Players>
</Root>");
var orders = from file1 in FILE1.Descendants("Players").Elements("Player")
select new Player(Int32.Parse(file1.Element("ClientId").Value), file1.Element("FirstName").Value, file1.Element("LastName").Value);
var orders2 = from file2 in FILE2.Descendants("Players").Elements("Player")
select new Player(Int32.Parse(file2.Element("ClientId").Value), file2.Element("FirstName").Value, file2.Element("LastName").Value);
//orders.Dump();
//orders2.Dump();
var exists = orders2.Intersect(orders, new LambdaEqualityComparer<Player>((i, j) => i.FirstName == j.FirstName && i.LastName == j.LastName));
// or
//var exists = orders2.Intersect(orders, new LambdaEqualityComparer<Player>((i, j) => i.ClientId == j.ClientId));
exists.Dump();
var notExists = orders2.Except(orders, new LambdaEqualityComparer<Player>((i, j) => i.FirstName == j.FirstName && i.LastName == j.LastName));
// or
//var notExists = orders2.Except(orders, new LambdaEqualityComparer<Player>((i, j) => i.ClientId == j.ClientId));
notExists.Dump();
}
public class Player
{
public int ClientId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Player(int clientId, string firstName, string lastName)
{
ClientId = clientId;
FirstName = firstName;
LastName = lastName;
}
}
public class LambdaEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, T, bool> EqualityComparer { get; set; }
public LambdaEqualityComparer(Func<T, T, bool> equalityComparer)
{
EqualityComparer = equalityComparer;
}
public bool Equals(T x, T y)
{
return EqualityComparer(x, y);
}
public int GetHashCode(T obj)
{
// If the hash codes are different, then Equals never gets called. Make sure Equals is always called by making sure the hash codes are always the same.
// (Underneath, the .NET code is using a set and the not (!) of a Find method to determine if the set doesn't already contain the item and should be added.
// Find is not bothering to call Equals unless it finds a hash code that matches.)
//return obj.GetHashCode();
return 0;
}
}
Note that if you don't want to create a Player
object, you can remove it completely, populate your anonymous objects in orders
and orders2
just like you were doing before, and create a new LambdaEqualityComparer<dynamic>
instead of new LambdaEqualityComparer<Player>
, though it will be slower due to reflection calls on dynamic.
来源:https://stackoverflow.com/questions/20218821/how-do-you-use-linq-to-xml-to-find-matching-nodes-in-two-different-xml-files