J'ai 2 listes: List et List .
Je dois utiliser la List comme source de vérité et renvoyer les voitures présentes et absentes dans la liste List .
Comment puis-je faire cela?
Mon approche:
var present = newCars.Where(c => oldCars.All(w => w.Id != c.Id)); var absent = newCars.Where(c => oldCars.All(w => w.Id == c.Id));
Je suis assez nouveau sur LINQ, et je ne suis pas sûr de la logique que j'ai utilisée ci-dessus.
Quelqu'un peut-il m'aider pour que cela fonctionne de manière plus efficace et optimisée?
Cela peut-il être fait en une seule requête et renvoyer 2 ensembles de résultats (sous forme de tuples)?
Je me rends compte qu'exécuter le même code avec des égaux et non des égaux que ci-dessus peut être une approche coûteuse.
4 Réponses :
Vous pouvez créer un IEqualityComparer personnalisé :
var present = newCars.Intersect(oldCars, new CarEqualityComparer()); var absent = newCars.Except(oldCars, new CarEqualityComparer());
Et l'utiliser comme ceci:
public class CarEqualityComparer : IEqualityComparer<Car>
{
public bool Equals(Car x, Car y)
{
return x.Id == y.Id;
}
public int GetHashCode(Car obj)
{
return obj.Id.GetHashCode();
}
}
Vous pouvez utiliser une variable anonyme contenant les deux listes:
var res = new
{
present = newCars.Where(n => oldCars.Select(o => o.Id).Contains(n.Id)), // present if the old cars list contains the id from the new list
absent = newCars.Where(n => !oldCars.Select(o => o.Id).Contains(n.Id)) // absent if the old cars list does not contain the id from the new list
};
Que faire si je dois vérifier non seulement l'identifiant de la voiture, mais également le numéro d'enregistrement. Existe-t-il un moyen d'avoir 2 conditions dans votre Select?
Lorsque vous dites que les nouvelles voitures sont la source de la vérité, cela signifie que vous les voulez toujours dans la liste, mais que vous n'avez peut-être pas une vieille voiture équivalente. Vous décrivez une jointure à gauche.
Cela devrait vous donner la plupart de ce que vous voulez.
var finalResult = (Present: results[0], Absent: results[1]);
Le résultat est un tuple contenant la nouvelle voiture et un Présentez qui est true ou false selon qu'il y a une vieille voiture ou non.
Cela vous donne un seul ensemble de résultats . Vous vouliez deux ensembles de résultats sous forme de tuple. Pour obtenir cela, étendez simplement la requête ci-dessus, en regroupant par Présent . Cela vous donne un ensemble de résultats avec exactement deux éléments
var results = (from r in (from n in newCars
join o in oldCars
on n.Id equals o.Id
into cars
from oc in cars.DefaultIfEmpty()
select (NewCar: n, Present: oc != null))
group r by r.Present into g
orderby g.Key descending
select g.Select(x => x.NewCar).ToList()).ToList();
à partir desquels vous pouvez obtenir un seul tuple contenant les deux listes
var results = from n in newCars
join o in oldCars
on n.Id == o.Id
group into cars
from oc in cars.DefaultIfEmpty()
select (NewCar: n, Present: oc != null)
p>
Je suggérerais de mettre un ToList sur le select final afin que g.Select soit traité une fois pendant la requête. De plus, je pense que votre première requête manque quelque chose: from oc.DefaultIfEmpty () n'a-t-il pas besoin d'une variable de plage? Et vous utilisez n et newcar comme la même variable de plage.
Qu'en est-il du de oc.DefaultIfEmpty () ? Il n'a pas besoin d'une variable de plage?
Je pense que vous étiez en train de regarder une édition précédente (c'était était faux à un moment donné). cars.DefaultIfEmpty () est ce qui est nécessaire pour obtenir des résultats corrects.
Non, première requête voir la phrase?
Oops. Je comprends enfin ce que vous essayez de dire. Je l'ai changé dans un bloc de code mais pas dans l'autre; c'est ce que j'obtiens pour ne pas utiliser de compilateur. Merci d'être diligent!
En utilisant une méthode d'extension pour traiter l'ajout à une liste (un tout petit peu) de manière plus fonctionnelle, vous pouvez utiliser LINQ Aggregate pour le faire en un seul passage:
var idsFromOldCars = oldCars.Select(c => c.Id).ToHashSet();
var present = new List<Car>();
var absent = new List<Car>();
foreach (var nc in newCars) {
if (idsFromOldCars.Contains(nc.Id))
present.Add(nc);
else
absent.Add(nc);
}
Où AfterAdd est défini dans une classe statique comme:
public static class ListExt {
public static List<T> AfterAdd<T>(this List<T> head, params T[] tail) {
head.AddRange(tail);
return head;
}
}
Mais il est beaucoup plus clair d'utiliser simplement un foreach boucle:
var idsFromOldCars = oldCars.Select(c => c.Id).ToHashSet(); var(present, absent) = newCars.Aggregate((p:new List<Car>(),a:new List<Car>()), (pa,nc) => idsFromOldCars.Contains(nc.Id) ? (pa.p.AfterAdd(nc),pa.a) : (pa.p,pa.a.AfterAdd(nc)));
Dans les deux cas, la création du HashSet garantit que vous n'avez pas à parcourir la liste oldCars pour trouver chaque newCar . Si vous saviez que les listes étaient classées par Id , vous pourriez faire une solution en un seul passage plus compliquée en parcourant les listes en parallèle, mais cela ne semble même pas valoir la complexité.
REMARQUE: Une Rejoindre fait effectivement quelque chose de similaire, en convertissant la deuxième Liste en une Recherche (comme un Dictionnaire ) qui peut être utilisé pour trouver les correspondances avec la première List.
Puis-je utiliser N'IMPORTE QUELLE à la place? Je peux avoir plus d'une condition à faire correspondre afin de les catégoriser à présenter ou absente? Aider.
@Illep Peut-être pouvez-vous mettre à jour votre question pour être plus claire? Vous semblez avoir des idées contradictoires sur ce que vous voulez ...
Equi-join et rejoindre gauche .
Si vous avez besoin de deux listes résultantes, utilisez une boucle for / foreach normale. Remplissez les deux en un seul passage.
examinez la méthode Intersect de la classe List. cela renverra des éléments communs dans 2 listes et renverra les résultats dans une nouvelle liste.
Tout d'abord, j'obtiendrais une liste de tous les objets
Carqui correspondent à l'aide de LINQ. Ensuite, vous pouvez utiliserList.Contains (item) == falsedans une boucleNe devrait pas être absent utiliser
Anyau lieu deAllVotre déclaration du présent et de l'absence semble ambiguë, sur la base de votre code, je suppose que vous voulez diviser la liste des
newCarsen voitures présentes dansoldCarset en voitures absentes deoldCars, auquel cas vous devriez avoirAnyau lieu deAll- évidemmentoldCars.Allne sera vrai que sioldCarscontient une voiture.