Need help with merging two data collections - c#

I need to retrieve all items from two lists that contains a given value.
Example:
var list1 = {
new Dummy(){ Name = "Dummy1", Number = 1 },
new Dummy(){ Name = "Dummy2", Number = 2 },
new Dummy(){ Name = "Dummy3", Number = 3 }
};
var list2 = {
new Dummy(){ Name = "Dummy4", Number = 4 },
new Dummy(){ Name = "Dummy5", Number = 2 },
new Dummy(){ Name = "Dummy6", Number = 6 }
};
var list3 = GetAllDummiesWithNumbersContainedInBothLists();
I want list3 to contain Dummy2 and Dummy5, since both have the same number.
How do i do this? It should be simple but i cant figure it out...

See if this works for you:
(from dummy1 in list1
join dummy2 in list2 on dummy1.Number equals dummy2.Number
from dummy in new[] { dummy1, dummy2 }
select dummy)
.Distinct()
This pairs matching dummies into the same scope, then flattens out the set so you get all of the matches in one sequence. The Distinct at the end ensures that each dummy appears exactly once even if either list contains repeated numbers.

I'm not entirely sure what your requirements are, but something like this perhaps?
var commonIds = list1.Select(d => d.Number)
.Intersect(list2.Select(d => d.Number));
var commonIdsSet = new HashSet<int>(commonIds);
var list3 = list1.Concat(list2)
.Where(d => commonIdsSet.Contains(d.Number))
.ToList();
if you can clarify the exact requirements (do the results need to be grouped by theNumber, IsNumberunique for an item within a list etc.), we can provide better solutions.

var list3 = list1.Where(d => list2.Select(d2 => d2.Number).Contains(d.Number))
.Union(list2.Where(d2 => list1.Select(d => d.Number).Contains(d2.Number)));

Here's one more!
var list3 = list1
.SelectMany(x => list2
.SelectMany(y =>
(y.Number == x.Number) ? new [] { x, y } : new Dummy[]{}
)
);

Related

SQL to LINQ expres

I'm trying to convert a SQL expression to Linq but I can't make it work, does anyone help?
SELECT
COUNT(descricaoFamiliaNovo) as quantidades
FROM VeiculoComSeminovo
group by descricaoFamiliaNovo
I try this:
ViewBag.familiasCount = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo).Count();
I need to know how many times each value repeats, but this way it shows me how many distinct values ​​there are in the column.
You can try:
var list = from a in db.VeiculoComSeminovo
group a by a.descricaoFamiliaNovo into g
select new ViewBag{
familiasCount=g.Count()
};
or
var list = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo)
.Select (g => new ViewBag
{
familiasCount=g.Count()
});
If you need column value:
new ViewBag{
FieldName=g.Key,
familiasCount=g.Count()
};
You don't need the GROUP BY unless there are fields other than the one in COUNT. Try
SELECT
COUNT(descricaoFamiliaNovo) as quantidades
FROM VeiculoComSeminovo
UPDATE, from your comment:
SELECT
COUNT(descricaoFamiliaNovo) as quantidades,
descricaoFamiliaNovo
FROM VeiculoComSeminovo
GROUP BY descricaoFamiliaNovo
That's it as SQL. In LINQ it is something like:
var reponse = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo)
.Select ( n => new
{Name = n.key,
Count = n.Count()
}
)
Not tested.
Ty all for the help.
I solved the problem using this lines:
// get the objects on db
var list = db.VeiculoComSeminovo.ToList();
// lists to recive data
List<int> totaisFamilia = new List<int>();
List<int> totaisFamiliaComSN = new List<int>();
// loop to cycle through objects and add the values ​​I need to their lists
foreach (var item in ViewBag.familias)
{
totaisFamilia.Add(list.Count(a => a.descricaoFamiliaNovo == item && a.valorSeminovo == null));
totaisFamiliaComSN.Add(list.Count(a => a.descricaoFamiliaNovo == item && a.valorSeminovo != null));
}
The query was a little slow than I expected, but I got the data

Intersect two object lists on a common property and then compare a different property

I have two lists
List<objA> List1
List<objA> List2
I want to compare these two list on ID field, once a match is found I want to compare another field Distace amongst these two lists and grab the object with the lower value.
Using Linq isn't is not giving the result I want, atleast for the first part of the problem.
var test = List1.Select(x => x.ID)
.Intersect(List2.Select(y => y.ID));
Here's one way you could do this with Linq. Firstly, join the two lists together with Union. Then, group them by the Id field. Lastly, order those sub lists by Distance within the grouping, and take the first one of each to get a list of objects by Id with the minimum available distance.
var aList = new[]
{
new SomeObject() { Id = 1, Distance = 3 },
new SomeObject() { Id = 2, Distance = 5 }
};
var bList = new[]
{
new SomeObject() { Id = 1, Distance = 2 },
new SomeObject() { Id = 2, Distance = 6 }
};
var results = aList
.Union(bList)
.GroupBy(a => a.Id, a => a)
.Select(a => a.OrderBy(b => b.Distance).First());

Linq to select highest ID where the ID is alphanumeric

I have a list of ID where the ID's start with MB (for members) or NM (for non members).
The ID would be like MB-101 or NM-108 etc...
I am trying to select the Highest ID starting with MB and then Add 1 and then save back to DB. Saving is easy but I am not sure how to query the highest Member or Nonmember ID and add one to it. Any help is much appreciated
You can do something like this:
List<string> list = new List<string>() { "MB-101", "MB-102", "MB-103", "MB-104"};
var ids = list.Select(x =>Convert.ToInt32(x.Replace("MB-", "")));//convert all the number parts to integer
list[list.FindIndex(x => x == "MB-" + ids.Max().ToString())] = "MB-" + (ids.Max() + 1);//set the max number after adding one.
You can do the same with your Nonmember ID. It is tested code, it successfully addresses your problem.
Hope it helps.
You can get the max id by splitting your list like below:
var ids = yourList.Where(x => x.ID.StartsWith("MB-")).Select(x => new { ID = x.ID.Split('-')[1] }).ToList();
var maxIdValue = ids.Select(x => int.Parse(x.ID)).ToList().Max();
If you want max id from both starting with MB- and NB- than you can remove above where condition. By this it will fetch max id from both MB- and NB-. Following will be query than:
var ids = yourList.Select(x => new { ID = x.ID.Split('-')[1] }).ToList();
var maxIdValue = ids.Select(x => int.Parse(x.ID)).ToList().Max();
you can try like this
List<string> lststr = new List<string>() { "MB-101", "MB-103", "MB-102", "NM-108" };
var result = "MB-"+ lststr
.Where(x=>x.Contains("MB"))
.Select(x => Regex.Replace(x, #"[^\d]", ""))
.OrderByDescending(x=>x)
.FirstOrDefault();
it will return MB-103 because it will first check if the string contains MB then it will replace everything with "" other than digit and OrderByDescending it will order by Descending so that the highest value will be on top and at last FirstOrDefault will get the fist value
You have to do OrderByDescending your list by replacing MB or NM with empty and get FirstOrDefault from ordered list. Please check below example.
CODE:
List<string> list = new List<string>() { "MB-101", "MB-102", "MB-109", "MB-105", "NM-110"};
var maxMember = list.OrderByDescending(m=>Convert.ToInt16(m.Replace("MB-","").Replace("NM-",""))).ToList().FirstOrDefault();
Console.WriteLine(maxMember.ToString());

Performing a union in LINQ

I'm trying to get the union of these two queries but keep getting the following error:
'System.Linq.IQueryable<AnonymousType#1>' does not contain a definition for 'Union' and the best extension method overload 'System.Linq.ParallelEnumerable.Union<TSource>(System.Linq.ParallelQuery<TSource>, System.Collections.Generic.IEnumerable<TSource>)' has some invalid arguments
The linq queries look like this:
var g = from p in context.APP_PROD_COMP_tbl
where p.FAM_MFG == fam_mfg
group p by new
{
a_B_G = p.B_G,
a_MFG = p.MFG,
a_PRODUCT_FAM = p.PRODUCT_FAM,
};
var q = from p in context.APP_COMP_tbl
where p.FAM_MFG == fam_mfg
group p by new
{
a_B_G = p.a_B_G,
a_MFG = p.a_MFG,
a_PRODUCT_FAM = p.a_PRODUCT_FAM,
};
var data = q.Union(g);
I've tried using IEnumerable around the queries, but it still didn't work. Not really sure where I'm going wrong at this point, although admittedly LINQ isn't something I've had a ton of exposure to.
Update:
So I've gone in a slightly different direction from what I posted earlier. After doing more research, the group by statements were from old code and no longer needed for the intended purpose. I changed those to select new statements and had no further issue with the union.
I think that your problem here is type mismatch: g is of type IGrouping<AnonymousType#1, APP_PROD_COMP_tbl> and q is of type IGrouping<AnonymousType#1, APP_COMP_tbl>; this is why Union gives you the error.
I am not really sure what you are trying to Union (keys of the groups or groups of data themselves) but the solution would be:
If you want to union group keys, select the keys of your groups
var data = g.Select(x => x.Key).Union(q.Select(x => x.Key));
If you want to union the groups themselves then you need to project each element from both sequences into a common type, perform the grouping and then union the groups
var g = context.APP_PROD_COMP_tbl
.Where(p => p.FAM_MFG == fam_mfg)
.Select(ToCommonType)
.GroupBy(p => new
{
a_B_G = p.B_G,
a_MFG = p.MFG,
a_PRODUCT_FAM = p.PRODUCT_FAM,
});
var q = context.APP_COMP_tbl
.Where(p => p.FAM_MFG == fam_mfg)
.Select(ToCommonType)
.GroupBy(p => new
{
a_B_G = p.a_B_G,
a_MFG = p.a_MFG,
a_PRODUCT_FAM = p.a_PRODUCT_FAM,
});
var data = g.Union(q);
private CommonClass ToCommonType(APP_PROD_COMP_tbl item)
{
return new CommonClass
{
};
}
private CommonClass ToCommonType(APP_COMP_tbl item)
{
return new CommonClass
{
};
}
The problem is your Anonymouse types don't match:
var a = Enumerable.Range(1, 10).Select(x => new {a = x}).AsQueryable();
var b = Enumerable.Range(1, 10).Select(x => new {b = x}).AsQueryable();
var c = a.Union(b);
This won't work because typeof a is not same as typeof b
var a = Enumerable.Range(1, 10).Select(x => new {a = x}).AsQueryable();
var b = Enumerable.Range(1, 10).Select(x => new {a = x}).AsQueryable();
var c = a.Union(b);
But this will work, because Anonymouse types are the same.
You can try selecting same anonymouse types from your collection in q and g. Read more about Union for IQueryable
Union on IQueryAble<TSource>() accepts IQueryAble<TSource> as a parameter, so collection has to be the same type.

Speed of linq query grouping and intersect in particular

Say 3 lists exist with over 500,000 records and we need to perform a set of operations (subsets shown below):
1) Check for repeating ids in list one and two and retrieve distinct ids while Summing up "ValuesA" for duplicate ids and put results in a list. Lets call this list list12.
2) compare all the values with matching ids between list 3 list12 and print results say to console.
3) ensure optimal performance.
This what i have so far:
var list1 = new List<abc>()
{
new abc() { Id = 0, ValueA = 50},
new abc() { Id = 1, ValueA = 40},
new abc() { Id = 1, ValueA = 70}
};
var list2 = new List<abc>()
{
new abc() { Id = 0, ValueA = 40},
new abc() { Id = 1, ValueA = 60},
new abc() { Id = 3, ValueA = 20},
};
var list3 = new List<abc>()
{
new abc() { Id = 0, ValueA = 50},
new abc() { Id = 1, ValueA = 40},
new abc() { Id = 4, ValueA = 70},
};
1) with the help of the solution from here [link][1] I was able to resolve part 1.
var list12 = list2.GroupBy(i => i.Id)
.Select(g => new
{
Id = g.Key,
NewValueA = g.Sum(j => j.ValueA),
});
2)I cant seem to properly get the complete result set from this part. I can get the matching account numbers, maybe someone knows of a faster way other than hashsets, but I also need the ValueA from each list along with the matching account numbers.
foreach (var values in list3.ToHashSet().Select(i => i.ID).Intersect(list12.ToHashSet().Select(j => j.UniqueAccount)))
{
Console.WriteLine(values) //prints matching account number
//?? how do I get ValueA with from both lists with this in the quickest way possible
}
3) my only attempt at improving performance from reading online is to use hashsets as I seen in the attempt above but I may be doing this incorrectly and someone may have a better solution
I don't think that any conversion to HashSet, however efficient, will increase performance. The reason is that the lists must be enumerated to create the HashSets and then the HashSets must be enumerated to get to the results.
If you put everything in one LINQ statement the number of enumerations will be minimized. And by calculating the sums at the end the number of calculations is reduced to the absolute minimum:
list1.Concat(list2)
.Join(list3, x => x.Id, l3 => l3.Id, (l12,l3) => l12)
.GroupBy (x => x.Id)
.Select(g => new
{
Id = g.Key,
NewValueA = g.Sum(j => j.ValueA),
})
With your data this shows:
Id NewValueA
0 90
1 170
I don't know if I understood all requirements well, but this should give you the general idea.
If you want to get access to both elements you probably want a join. A join is a very general construct that can be used to construct all other set operations.

Categories

Resources