Compare lists and get values with Linq - c#

My object is "MyClass" with 2 properties : Id (int) and HourBy (int)
I have two list :
var List1 = new List<MyClass>();
var List2 = new List<MyClass>();
I'd like to get one list with :
- Object from List1 present in List2 based on Id with Hourby (list2) < Hourby in List1
- Object from List1 no present in List2
//#Sample1
//List1 :
List1.add(new MyClass(1,10));
List1.add(new MyClass(2,20));
List1.add(new MyClass(3,30));
//List2 :
List2.add(new MyClass(1,10));
List2.add(new MyClass(2,15));
//I'd like to get :
new MyClass(2,5);
new MyClass(3,30);
//Sample2
List1 :
List1.add(new MyClass(1,10));
List1.add(new MyClass(2,20));
List1.add(new MyClass(3,30));
//List2 :
List2.add(new MyClass(1,10));
List2.add(new MyClass(2,15));
List2.add(new MyClass(2,2));
//I'd like to get :
new MyClass(2,3);
new MyClass(3,30);
Thanks,

You can do this by the following Linq statement:
var result = List1.Where(x => !List2.Select(y => y.Id).Contains(x.Id)
|| List2.Single(y => x.Id == y.Id).HourBy < x.HourBy);
Best Regards

I think this is what you're after, based on the example lists you supplied...
var list3 = new List<MyClass>();
foreach(var item in list1)
{
var totalFromOther = list2.Where(x => x.id == item.id).Sum(y => y.HourBy);
var newItem = new MyClass() { id = item.Id, HourBy = item.HourBy - totalFromOther };
if(newItem.HourBy > 0)
list3.Add(newItem);
}

Here's a solution:
var qry = from ee in
(from e1 in list1
join e2 in list2 on e1.Id equals e2.Id into gj
from e in gj.DefaultIfEmpty()
select new {
e1.Id,
MainHourBy = e1.HourBy,
SecondHourBy = (e == null ? 0 : e.HourBy)
})
where ee.SecondHourBy < ee.MainHourBy
group ee by ee.Id into g
select new MyClass
{
Id = g.Key,
HourBy = g.First().MainHourBy - g.Sum(el => el.SecondHourBy)
};

I use this code, it's not perfect but working and may be clearer for newbie in Linq (like me)
List< MyClass> listRes = new List< MyClass>();
foreach ( MyClass item in list1)
{
var exist = list2
.Select(x => x.MyClass)
.Contains(item);
if (!exist)
listRes.Add(item);
else
{
var totalFromOther = list2
.Where(x => x.MyClass.Id == item.Id)
.Sum(y => y.HourBy);
if (item.HourBy != totalFromOther)
{
item.HourBy = item.HourBy - totalFromOther;
listRes.Add(item);
}
}
}
Thanks for you help,

var agg1 = from l in list1
group l by l.ID into g
select new { g.Key, Sum = g.Sum(_ => _.HourBy) };
var agg2 = from l in list2
group l by l.ID into g
select new { g.Key, Sum = g.Sum(_ => _.HourBy) };
var q = from l1 in agg1
let l2 = agg2.FirstOrDefault(_ => _.Key == l1.Key)
let sum = l1.Sum - (l2 != null ? l2.Sum : 0)
where sum != 0
select new MyClass(l1.Key, sum);
var list3 = q.ToList();

//Group list 2, so there is one entry per ID, with HourBy = total hour by
var summedL2 = from item2 in List2
group item2 by item2.ID into g
select new Test(g.Key, g.Sum(item => item.HourBy));
var result =
//Select items from list1
from item1 in List1
//Left join to our summed List2
join item2s in summedL2 on item1.ID equals item2s.ID into tempItem2s
from item2 in tempItem2s.DefaultIfEmpty()
//Where there is no match, or item2 is < item1
where (item2 == null || item2.HourBy < item1.HourBy)
//Select a new Test object, with the HourBy changed
select new Test(item1.ID, item1.HourBy - (item2 == null ? 0 : item2.HourBy));

Related

Looking for an elegant way to compare two lists and show differences from either side

Let's say I have two lists of strings.
List 1
- Item Abc
- Item Xyz
- Item BlahBlah
List 2
- Item Abc
- Item Xyz
- Item YadiYada
I want to show a table of matches and missing matches, something like this:
List 1 | List 2
----------------------------------
Item Abc | Item Abc
Item Xyz | Item Xyz
Item BlahBlah |
| Item YadiYada
I was thinking this could be done elegantly with LINQ, but I wasn't quite sure how to tackle it. I'd appreciate some direction on this.
Try this:
var leftList = new List<string>() { "1", "2", "3" };
var rightList = new List<string>() { "2", "3", "4" };
var left = leftList.Except(rightList).Select(e => new { L = e, R = string.Empty });
var right = rightList.Except(leftList).Select(e => new { L = string.Empty, R = e });
var intersection = leftList.Intersect(rightList).Select(e => new {L = e, R = e});
var result = intersection.Union(left).Union(right).ToList();
Just another solution:
var list1 = new List<string> { "Abc", "Xyz", "BlahBlah" };
var list2 = new List<string> { "Abc", "Xyz", "YadiYada" };
var r1 = from l1 in list1
join l2 in list2 on l1 equals l2 into t
from l2 in t.DefaultIfEmpty()
select new { l1, l2 };
var r2 = from l2 in list2
join l1 in list1 on l2 equals l1 into t
from l1 in t.DefaultIfEmpty()
select new { l1, l2 };
var result = r1.Union(r2);
Left Outer Join
This is an alternative using method syntax:
var list1 = new List<string> { "Abc", "Xyz", "BlahBlah" };
var list2 = new List<string> { "Abc", "Xyz", "YadiYada" };
var r1 = list1
.GroupJoin(
list2,
l1 => l1,
l2 => l2,
(l1, l2) => new { l1 = l1, l2 = l2.FirstOrDefault() });
var r2 = list2
.GroupJoin(
list1,
l2 => l2,
l1 => l1,
(l2, l1) => new { l1 = l1.FirstOrDefault(), l2 = l2 });
var result = r1.Union(r2);
LINQ Query Syntax versus Method Syntax
In general, we recommend query syntax because it is usually simpler
and more readable; however there is no semantic difference between
method syntax and query syntax. In addition, some queries, such as
those that retrieve the number of elements that match a specified
condition, or that retrieve the element that has the maximum value in
a source sequence, can only be expressed as method calls.

Remove Duplicate/Existing value from dropdownlist

I will like to generate a dropdownlist with unique value.
However i only able to generate an original dropdownlist which generated from database.
At my language table it contain 3 languages:
-ENG , GER, KOR
for the language i already used for a question list:
- KOR
So the result bind to dropdownlist should:
- ENG, GER
My Controller code:
List<SelectListItem> langResult = new List<SelectListItem>();
///// Ori Language
var result = from r in db.SURV_Language_Model
select r.Language;
///// Used Language
var result2 = from r in db.SURV_Question_Ext_Model
join s in db.SURV_Question_Model on r.Qext_Question_ID equals s.Question_ID
orderby s.Question_Position ascending
where r.Qext_Question_ID == Question_ID
select new { r, s };
/////Remaining Language
result 3 = result 2 - result 1 <==*** Pseudo code
foreach (var item in result3)
{
SelectListItem temp = new SelectListItem();
temp.Text = item;
temp.Value = item;
langResult.Add(temp);
}
ViewBag.LangList = langResult;
My View Code:
#Html.LabelFor(model => model.Language)
#Html.DropDownListFor(model => model.Language, ViewBag.LangList as IEnumerable<SelectListItem>)
#Html.ValidationMessageFor(model => model.Language, "*")
Anyway to perform this?
Was fixed by code below, hope can help you all:
List<SelectListItem> langResult = new List<SelectListItem>();
var result = from r in db.SURV_Language_Model
select r.Language;
var result2 = from r in db.SURV_Question_Ext_Model
join s in db.SURV_Question_Model on r.Qext_Question_ID equals s.Question_ID
orderby s.Question_Position ascending
where r.Qext_Question_ID == Question_ID
select r.Qext_Language;
List<string> list = new List<string>(result);
List<string> list2 = new List<string>(result2);
for (int x = 0; x < list.Count(); x++ )
{
if ( x < list2.Count())
{
if (list[0].ToString() == list2[x].ToString())
{
list.Remove(list[0].ToString());
}
}
}
foreach (var item in list)
{
SelectListItem temp = new SelectListItem();
temp.Text = item;
temp.Value = item;
langResult.Add(temp);
}
ViewBag.LangList = langResult;

How to do a String.Replace in LINQ?

Here is what I am trying to do but with no success. I want to call from x in list1 and join y in list2 where regex.Match(x.Value).Success. After these steps I need to call String.Replace twice on x.Value. Then call the on and select operators. I want it to look something like the second example. How can this be acheived?
Here is how my lists look like:
list1 list2
Name Value Name Value
item.1 $(prod1) prod1 prodVal1
item.2 $(prod2) prod2 prodVal2
item.3 prod3 prod3 prodVal3
Here is how my lists should look like:
updatedList
Name Value
item.1 prodVal1
item.2 prodVal2
item.3 prod3
Example 1 (This is what I currently have):
foreach (var x in list1)
{
Match match = reg.Match(x.Value);
if (match.Success)
{
x.Value = x.Value.Replace("$(", "");
x.Value = x.Value.Replace(")", "");
}
}
var commonItems = from x in list1
join y in list2
on x.Value equals y.Name
//where regex.Match(x.Value).Success
select new { Item = x, NewValue = y.Value };
foreach (var x in commonItems)
{
x.Item.Value = x.NewValue;
}
Example 2:
var commonItems = from x in list1
join y in list2
where regex.Match(x.Value).Success
//do x.Value.Replace("$(", "")
//do x.Value.Replace(")", "")
//then call
on x.Value equals y.Name
select new { Item = x, NewValue = y.Value };
foreach (var x in commonItems)
{
x.Item.Value = x.NewValue;
}
If I understood correctly what you want is to use let in your query:
var commonItems = from x in list1
join y in list2
let newX = x.Value.Replace("$(", "").Replace(")", "")
where regex.Match(x.Value).Success
&& newX == y.Name
select new { Item = newX, NewValue = y.Value };
Are you sure you need Regex.Match? you could obtain the same result without using it, anyway I added both versions...
In the 1° version you can use a simple If statement to check if the value was changed
NewValue = x.Value != x1 ? y.Value: x.Value
Sample code
given this class
class MyClass
{
public string Name { get; set; }
public string Value { get; set; }
}
.
adding items
var list1 = new List<MyClass>();
list1.Add(new MyClass { Name = "item.1", Value = "$(prod1)" } );
list1.Add(new MyClass { Name = "item.2", Value = "$(prod2)" });
list1.Add(new MyClass { Name = "item.3", Value = "prod3" });
var list2 = new List<MyClass>();
list2.Add(new MyClass { Name = "prod1", Value = "prodVal1" });
list2.Add(new MyClass { Name = "prod2", Value = "prodVal2" });
list2.Add(new MyClass { Name = "prod3", Value = "prodVal3" });
getting common list
var q = from x in list1
let x1 = x.Value.Replace("$(", "").Replace(")", "")
join y in list2 on x1 equals y.Name
select new {
Item = x.Name,
NewValue = x.Value != x1 ? y.Value: x.Value
};
foreach (var s in q)
{
Console.WriteLine(s.Item + " " + s.NewValue);
}
result
item.1 prodVal1
item.2 prodVal2
item.3 prod3
PS: I don't think you need Regex, but just in case this version will work using it.
var q = from x in list1
let x1 = x.Value.Replace("$(", "").Replace(")", "")
join y in list2 on x1 equals y.Name
select new
{
Item = x.Name,
NewValue = Regex.Match(x.Value, x1).Success ? x.Value : y.Value
};

how to get the number of repetitions from List<int>

List<int> ListIdProducts = new List<int>();
var IdProductKey = from a in me.ProductKeywords where a.Keyword == item.Id select a;
foreach (var item2 in IdProductKey)
{
ListIdProducts.Add(item2.Product.Value);
}
Result is:
5
6
7
5
2
5
I need to get the following 5=3, 6=1, 7=1, 2=1
Use GroupBy LINQ method:
ListIdProducts
.GroupBy(i => i)
.Select(g => new { Value = g.Key, Count = g.Count() });
var query1 = from a in ListIdProducts
group a by new { a } into g
select new
{
item = g.Key,
itemcount = g.Count()
};
This a fairly standard group-by problem.
//untested
var IdProducts = from a in me.ProductKeywords
where a.Keyword == item.Id
group by a.Product.Value into g
select g.Count();

LINQ Filter anonymous type based on IEnumerable values within type

I'm using LINQ to SQL like:
var b =
from s in context.data
select new
{
id = s.id,
name = s.name
myEnumerable = s.OneToMany
};
Where myEnumerable is of type IEnumberable<T> and I want to now get a subset of b based upon properties of the individual items of myEnumerable. For example, say <T> has properties Berry and BerryID, I would want to do something like:
b =
from p in b
where //p.myEnumerable.myType.BerryID== 13
select p;
I'm feel like I'm missing something easy...
Since myEnumerable is an IEnumerable you will have to do a where on that.
var filteredData = from p in listOfData
where p.InnerData.Where(b=>b.ID == 13).Count() > 0
select p;
If I understand what you are saying...this is if there is an ID = 13 in the Enumerable at all.
Are you looking to select p if any of the items in p.myEnumerable have BerryID equal to 13?
b = from p in b
where p.myEnumerable.Any(t => t.BerryID == 13)
select p;
Or are you looking to select p if all of the items in p.myEnumerable have BerryID equal to 13?
b = from p in b
where p.myEnumerable.All(t => t.BerryID == 13)
select p;
What exactly is the condition you want the items in p.myEnumerable to fulfill before you select p?
Keep only items with at least one item having BerryID equal to 13 in the collection.
var b = context.data
.Where(s => s.OneToMany.Any(i => i.BerryID == 13))
.Select(s => new { id = s.id, name = s.name, myEnumerable = s.OneToMany });
Keep only items with all item having BerryID equal to 13 in the collection.
var b = context.data
.Where(s => s.OneToMany.All(i => i.BerryID == 13))
.Select(s => new { id = s.id, name = s.name, myEnumerable = s.OneToMany });

Categories

Resources