How to modify an object depends on join condition in linq - c#

I have a list of objects and alist of integers. Objects have property called exists.
public bool exists;
I need to join this two lists using left join and if ids exist in two lists, then set "exists" property to true;
I prepred dotnetfiddle example:
https://dotnetfiddle.net/sE1RIl
Expected result is (pseudocode):
Item1.exists = true;
Item2.exists = true;
Item3.exists = false;
Probably I will need to add more left joins later, so I am interested in the most flexible way to achieve that.

You can map your properties using select statement:
IEnumerable<Item> items = new List<Item>()
{
new Item (){id =1, name = "Item1"},
new Item (){id =2, name = "Item2"},
new Item (){id =3, name = "Item3"}
};
List<int> ids = new List<int>() {1,2};
var param_1 = true;
var param_2 = false;
var param_3 = true;
var listOfItems = from item in items
join id in ids on item.id equals id
into result
from r in result.DefaultIfEmpty()
select new Item
{
id = item.id,
name = item.name,
exists = (param_1 == true) ? true
: (param_2 == false && param_3 == true) ? false
: true
};

Related

How to add item to IEnumerable SelectListItem

I am trying to add an item to an IEnumerable SelectList. I have an initial query that populates my list, I then have a query to check to see if an item called "INFORMATIONAL" exists. If not, I need to add it to the list returned from my initial query. Here is my code. It does not like list.Add(newItem). Any assistance would be appreciated. Thanks
public IEnumerable<SelectListItem> GetCategoriesByAccountID(string AccountID)
{
IEnumerable<SelectListItem> list = null;
using (var context = new AMPEntities())
{
// Queries DB for list of categories by AccountID
var query = (from ca in context.CustomAlerts
where ca.AccountID == AccountID
orderby ca.AlertCategory
select new SelectListItem { Text = ca.AlertCategory, Value = ca.AlertCategory }).Distinct();
list = query.ToList();
// Checks list to see if "INFORMATIONAL" already exists
var item = (from l in list
where l.Value == "INFORMATIONAL"
select new SelectListItem { Text = l.Text, Value = l.Value }).FirstOrDefault();
// If "INFORMATIONAL" is not present add it to list
if (item == null)
{
var newItem = new SelectListItem { Text = "INFORMATIONAL", Value = "INFORMATIONAL" };
list.Add(newItem);
}
}
return list;
}
The problem is that your variable is of type IEnumerable<SelectListItem>. Either change it to List<SelectListItem> or use another variable.
public IEnumerable<SelectListItem> GetCategoriesByAccountID(string AccountID)
{
List<SelectListItem> list = null;
using (var context = new AMPEntities())
{
// Queries DB for list of categories by AccountID
var query = (from ca in context.CustomAlerts
where ca.AccountID == AccountID
orderby ca.AlertCategory
select new SelectListItem { Text = ca.AlertCategory, Value = ca.AlertCategory }).Distinct();
list = query.ToList();
// Checks list to see if "INFORMATIONAL" already exists
var item = (from l in list
where l.Value == "INFORMATIONAL"
select new SelectListItem { Text = l.Text, Value = l.Value }).FirstOrDefault();
// If "INFORMATIONAL" is not present add it to list
if (item == null)
{
var newItem = new SelectListItem { Text = "INFORMATIONAL", Value = "INFORMATIONAL" };
list.Add(newItem);
}
}
return list;
}
Essentially, you cannot, because IEnumerable does not necessarily represent a collection to which items can be added. See this question on SO.
How can I add an item to a IEnumerable<T> collection?
Here is something I came up with that might be helpful to someone. Maybe even me at a later date. Take a look at the last line of code.
CostCenterHeaders CostHeaders = CostCenterHeaders.GetCostCenterHeaders(ClientNumber);
List<SelectListItem> Level1Header = new List<SelectListItem>();
if (CostHeaders.Level1Heading !=null)
{
Level1Header.Add(new SelectListItem { Text = "All " + CostHeaders.Level1Heading + " Centers", Value = "" });
List<HierarchyLevel> HierarchyLevels = HierarchyLevel.GetHierarchyByLevel(ClientNumber);
Level1Header.AddRange(HierarchyLevels.Select(x => new SelectListItem() { Value = x.LevelID, Text = x.LevelDescr }).ToList());
}

Grouping in a generic list

I have a generic list:
List<Test> lstReport = new List<Test>();
lstReport.Add(new Test { ID = 1, Category = "Hot work", Approver = "Praveen" });
lstReport.Add(new Test { ID = 1, Category = "Civil work", Approver = "Praveen" });
lstReport.Add(new Test { ID = 1, Category = "Others", Approver = "Praveen" });
lstReport.Add(new Test { ID = 4, Category = "Hot work", Approver = "Praveen" });
lstReport.Add(new Test { ID = 5, Category = "Critical work", Approver = "Praveen" });
Now I want to take unique rows from the generic list by the ID value.
For eg, I need to group the rows having same ID and if the existence of ID's more than once, then its Category field has to be changed to "Grouped".
How can I write this expression?
This Works:
var QueryResult = (from x in lstReport
group x by x.ID into res
select new Test
{
ID = res.Key,
Category = res.Count() > 1 ? "Grouped" : res.First().Category,
Approver = res.First().Approver
}).ToList();
List<Test> lstReport = new List<Test>();
...
var groups = lstReport.GroupBy(i => i.ID).ToList();
foreach(var group in groups) {
var items = group.ToList();
if(items.Count > 1)
items.ForEach(i => { i.Category = "Grouped"; });
}
You can try this
var q = from r in lstReport
group r by r.ID into g
select new Test{ID=g.Key, Category = g.Count()>1? "Grouped" : g.Min(Category)};
also you can have g.Max(...) or g.Min(...)
You can use Linq to group objects based on some property:
var list = lstReport.GroupBy(t => t.ID);
This will return a new collection with 3 items: IEnumerable<IGrouping<TKey, TSource>>; which basically means a collection containing a collection with one or more items.
Then you can iterate over that new collection and check if each item contains more than one subitem; if it does, then iterate over the subitems and modify the Category value.
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.groupby(v=vs.110).aspx

Sorting a dropdownlist

I'm working with entity framework database and I want to populate my dropdownlist from a table but in right order.
Right now i'm using the code:
var databaseList = from p in db.TECHNICAL_SKILLS
where p.skill_type == "Database"
select new EmployeeTechnicalSkillInfo
{
TechnicalSkillId = p.technical_skill_id,
SkillType = p.skill_type,
SkillName = p.skill_name
};
List<object> sDataValue = new List<object>();
sDataValue.Add("- Select -");
sDataValue.Remove("Other");
foreach (var vData in databaseList)
{
sDataValue.Add(vData.SkillName);
}
sDataValue.Add("Other");
DropDownListDB.DataSource = sDataValue.ToArray();
DropDownListDB.DataBind();
This is the Solution
var databaseList = from p in db.TECHNICAL_SKILLS
where p.skill_type == "Database"
orderby p.skill_name != "Other" descending, p.skill_name
select new EmployeeTechnicalSkillInfo
{
TechnicalSkillId = p.technical_skill_id,
SkillType = p.skill_type,
SkillName = p.skill_name
};
Remove the entry -Select- from the list and append it as first item to your List<object>:
List<object> sDataValue = new List<object>();
sDataValue.Add("-Select-");
foreach (var vData in databaseList){}
would be the easiest way, I assume.
EDIT
then you will use if/else, maybe and
foreach (var vData in databaseList){} //fill your list
sDataValue.Add("-Other-");
You could add a "section id" to your database: 0 for "-Select-", 2 for "Other", 1 for the rest. Then sort on 1) section ID, 2) name.
But it would be best not to store that "-Select-" at all (it doesn't represent a valid value) and add it in the correct spot after the databind.
Just do this:
Remove -Select- and Other from databaseList like this:
var databaseList = from p in db.TECHNICAL_SKILLS
where p.skill_type == "Database"
&& p.skill_name.ToLower() !="other"
&& p.skill_name.ToLower() !="-select-"
select new EmployeeTechnicalSkillInfo
{
TechnicalSkillId = p.technical_skill_id,
SkillType = p.skill_type,
SkillName = p.skill_name
};
and then do this:
DropDownListDB.DataSource = sDataValue.ToArray().OrderBy(s=>s);
DropDownListDB.Items.Insert(0, "-Select-");
DropDownListDB.Items.Add("Other");
if you must bring -Select- and Other field from database, binding them to the DropDownList and then removing them and again inserting them at appropriate location is little expensive.
You simply don't pick those two rows from the database. it will be more efficient.
You can try play with orderby with boolean value
orderby p.skill_name != "Other" descending, p.skill_name
In your code =>
var databaseList = from p in db.TECHNICAL_SKILLS
where p.skill_type == "Database"
orderby p.skill_name != "Other" descending, p.skill_name
select new EmployeeTechnicalSkillInfo
{
TechnicalSkillId = p.technical_skill_id,
SkillType = p.skill_type,
SkillName = p.skill_name
};
List<object> sDataValue = new List<object>();
sDataValue.Add("- Select -");
foreach (var vData in databaseList)
{
sDataValue.Add(vData.SkillName);
}
DropDownListDB.DataSource = sDataValue.ToArray();
DropDownListDB.DataBind();

Not Equal two Generic Lists

I have two generic lists. They have mostly different fields, but there are 4 fields they have in common. I want to get the list of items that are in one of the lists but not the other using those four fields as the definition of "equality".
Here was my attempt at solving the problem.
var unMatchedData = from liveLines in liveList
join oldList in comapreSnapshotList
on new {liveLines.ClientNo, liveLines.SequenceNo, liveLines.LineNo, liveLines.Text} equals
new {oldList.ClientNo, oldList.SequenceNo, oldList.LineNo, oldList.Text}
select new KNOWTXTS
{
ClientNo = liveLines.ClientNo,
SequenceNo = liveLines.SequenceNo,
LineNo = liveLines.LineNo,
Text = liveLines.Text
};
You can use Except to find the set difference.
var newElements = liveList.Select(l => new {
l.ClientNo, l.SequenceNo, l.LineNo, l.Text
});
var oldElements = comapreSnapshotList.Select(l => new {
l.ClientNo, l.SequenceNo, l.LineNo, l.Text
});
var newElementsInNew = newElements.Except(oldElements);
var deletedFromNew = oldElements.Except(newElements);
// if you need the original object in the list
var newElements = from obj in liveList
join newEle in newElementsInNew
on new {obj.ClientNo, obj.SequenceNo, obj.LineNo, obj.Text} equals newEle
select obj;
var deletedElements = from obj in comapreSnapshotList
join deletedEle in deletedFromNew
on new {obj.ClientNo, obj.SequenceNo, obj.LineNo, obj.Text} equals deletedEle
select obj;

Need a linq join that also compares properties

I have two lists of entities. Imagine list1 is remote and list2 is local - list1 was created some time in the past and list2 has just been generated.
I want to compare both lists, matching by .id, and comparing the .flag property only of each element. Where the .flag property is different, I want to select the older element, but with the .flag property from list2 (The new list).
The example below shows how to select just the entities in list1 that are different. How can I select the entities from list1 that are different, but using the .flag property from the list2 entity.
Note: I don't want to select new SomeEntity(){} an entire SomeEntity class as in the real problem, the classes I'm working with have a lot of properties.
class SomeEntity
{
public int id;
public bool flag;
public int some_value = -1;
}
// Setup the test
List<SomeEntity> list1 = new List<SomeEntity>();
List<SomeEntity> list2 = new List<SomeEntity>();
for (int i = 0; i < 10; i++ )
{
list1.Add(new SomeEntity() { id = i, flag = true, some_value = i * 100 });
list2.Add(new SomeEntity() { id = i, flag = true, });
}
// Toggle some flags
list1[3].flag = false;
list2[7].flag = false;
// Now find the entities that have changed and need updating
var items_to_update = from x in list1
join y in list2 on x.id equals y.id
where x.flag != y.flag
select x;
You can add this to your code, after retrieving the items_to_update collection:
foreach (var item in items_to_update)
{
item.flag = list2.Where(c => c.id == item.id).SingleOrDefault().flag;
}
How can I select the entities from list1 that are different, but using the .flag property from the list2 entity.
and
I don't want to select new SomeEntity(){}
Means that you want to modify entity in list1 before return. Linq is not clear tool for do that.
foreach (var item in from x in list1
join y in list2 on x.id equals y.id
where x.flag != y.flag
select new {x, y})
{
item.x.flag = item.y.flag
yield return item.x;
}

Categories