Ho due array ..
var data1 = new[] {
new { Product = "Product 1", Year = 2009, Sales = 1212 },
new { Product = "Product 2", Year = 2009, Sales = 522 },
new { Product = "Product 1", Year = 2010, Sales = 1337 },
new { Product = "Product 2", Year = 2011, Sales = 711 },
new { Product = "Product 2", Year = 2012, Sales = 2245 },
new { Product = "Product 3", Year = 2012, Sales = 1000 }
};
var data2 = new[] {
new { Product = "Product 1", Year = 2009, Sales = 1212 },
new { Product = "Product 1", Year = 2010, Sales = 1337 },
new { Product = "Product 2", Year = 2011, Sales = 711 },
new { Product = "Product 2", Year = 2012, Sales = 2245 }
};
Quello che voglio fare è controllare ogni distinto Product
e Year
in data2
e se esiste una riga per qualsiasi combinazione di tali Product
e Year
in data1
ma non in data2
quindi aggiungi quella riga a data2
.
Esempio.. Nel data2
, i prodotti distinti sono Product1
e Product2
e anni distinti sono Year1
, Year2
, Year3
e Year4
.
In data1 esiste una riga { Product = "Product 2", Year = 2009, Sales = 522 }
, che non è presente in data2
, quindi desidero aggiungerlo a data2
.
Quello che posso fare è ottenere prodotti e anni distinti in due variabili.
Quindi esegui un ciclo per ogni ciclo in entrambi e controlla se la combinazione esiste in data1 ma non in data2 e, in tal caso, aggiungila a data2.
Quello che vorrei ottenere è una singola query LINQ che può fare questo lavoro per me invece di farne due distinte separatamente e poi eseguirne un paio per ogni ciclo.
Grazie
risposte:
2 per risposta № 1Puoi farlo funzionare in una singola query. Tuttavia non sarà ottimale, perché per ogni elemento in data1
dovresti controllare tre condizioni, che potenzialmente richiedono di essere esaminate interamente data2
per una complessità temporale O (m * n) (la complessità spaziale rimane O (1), però).
Puoi evitare lo stesso ciclo, però:
var uniqueProd = new HashSet<string>(data2.Select(d=>d.Product));
var uniqueYear = new HashSet<int>(data2.Select(d=>d.Year));
var knownPairs = new HashSet<Tuple<string,int>>(
data2.Select(d=>Tuple.Create(d.Product, d.Year))
);
var newData2 = data2.Concat(
data1.Where(d =>
uniqueProd.Contains(d.Product) // The product is there
&& uniqueYear.Contains(d.Year) // The year is there
&& !knownPairs.Contains(Tuple.Create(d.Product, d.Year)) // Combination is not there
)
).ToArray();
Questa soluzione è O (m + n) nel tempo e anche O (n) nello spazio.
1 per risposta № 2
Non avrò alcuna pretesa di efficienza, ma è possibile in una singola query.
Se ti accontenti di lasciare che Union gestisca la rimozione dei duplicati, puoi fare:
var newd2 = data2.Union(
from d1 in data1
where
(from d2p in data2 from d2y in data2
select new { d2p.Product, d2y.Year })
.Distinct().Any(mp => mp.Product == d1.Product && mp.Year == d1.Year)
select d1);
In alternativa, puoi escludere corrispondenze data2 preesistenti e utilizzare Concat
var newd2 = data2.Concat(
from d1 in data1
where
(from d2p in data2 from d2y in data2 select new { d2p.Product, d2y.Year })
.Distinct().Any(mp => mp.Product == d1.Product && mp.Year == d1.Year) &&
!data2.Any(mp => mp.Product == d1.Product && mp.Year == d1.Year)
select d1
);
OTOH, non ho potuto resistere ad alcuni tempi.Se chiamiamo l'utilizzo di Union come 1, l'utilizzo di Concat varia dal 73% delle volte, la creazione di HashSet utilizza l'827% delle volte e l'estrazione della coppia unica esposta richiede il 54% e saltare il .Distinct () richiede il 27%, sebbene il set di dati è troppo lento per distinguere le differenze in alcuni di questi.
Estrarre le coppie e scaricare Distinct:
var newdd = (from d2p in data2 from d2y in data2 select new { d2p.Product, d2y.Year });
var newd2 = data2.Concat(
from d1 in data1
where
newdd.Any(mp => mp.Product == d1.Product && mp.Year == d1.Year) &&
!data2.Any(mp => mp.Product == d1.Product && mp.Year == d1.Year)
select d1
);