Convert to LINQ - c#

I have a list containing another list. I want to loop inside the primary list and then inside all the secondary lists, until the id of an object from the secondary list matches the required id. The primary list should then be returned.
Is it possible to convert the following code to LINQ?
var myID = "1"; //Just a stub
var tempItem = null;
//Loop each itemA from list A
foreach (var itemA in listA)
{
//Loop each itemB from itemA.listB
foreach (var itemB in itemA.listB)
{
//Compare IDs
if (itemB.ID == myID)
{
//If IDs of itemB match, return itemA
tempItem = itemA;
break;
}
}
}

tempItem = listA.FirstOrDefault(a=> a.itemB.Any(b=>b.ID==myID))

This will return listA item or null if no list is found:
var tempItem = listA.FirstOrDefault(l => l.listB.Exists(x => x.ID == myID));

I think you should try the following code
var result = itemA.FirstOrDefault(p => p.listB.Find(t => t.ID == myID) != null));
This should return null if no matching items are found. Otherwise, will return the first matching object. To get a collection of matching objects, you just have to replace the FirstOrDefault method with Where

foreach (var itemA in ListA.Where(itemA => itemA.B.Any(itemB => itemB.ID == myID)))
{
tempItem = itemA;
}

Related

How to remove duplicates from object list based on that object property in c#

I've got a problem with removing duplicates at runtime from my list of object.
I would like to remove duplicates from my list of object and then set counter=counter+1 of base object.
public class MyObject
{
MyObject(string name)
{
this.counter = 0;
this.name = name;
}
public string name;
public int counter;
}
List<MyObject> objects_list = new List<MyObject>();
objects_list.Add(new MyObject("john"));
objects_list.Add(new MyObject("anna"));
objects_list.Add(new MyObject("john"));
foreach (MyObject my_object in objects_list)
{
foreach (MyObject my_second_object in objects_list)
{
if (my_object.name == my_second_object.name)
{
my_object.counter = my_object.counter + 1;
objects_list.remove(my_second_object);
}
}
}
It return an error, because objects_list is modified at runtime. How can I get this working?
With a help of Linq GroupBy we can combine duplicates in a single group and process it (i.e. return an item which represents all the duplicates):
List<MyObject> objects_list = ...
objects_list = objects_list
.GroupBy(item => item.name)
.Select(group => { // given a group of duplicates we
var item = group.First(); // - take the 1st item
item.counter = group.Sum(g => g.counter); // - update its counter
return item; // - and return it instead of group
})
.ToList();
The other answer seem to be correct, though I think it will do scan of the whole list twice, depending on your requirement this might or might not be good enough. Here is how you can do it in one go:
var dictionary = new Dictionary<string, MyObject>();
foreach(var obj in objects_list)
{
if(!dictionary.ContainsKey(obj.name)
{
dictionary[obj.name] = obj;
obj.counter++;
}
else
{
dictionary[obj.name].counter++;
}
}
Then dictionary.Values will contain your collection

can not convert from System.Collection.Generic.List<Dal.Questio> to Dal.Questio

I want to append my list into another list but when i do this i got the error
How can it be solved?
public List<Questio> Get(int Id)
{
List<Questio> Quest;
Quest = null;
try
{
using (db = new Entities())
{
var Qu = db.Que.Where(x => x.Lan == Id).Select(i => i.Id);
foreach (var a in Qu)
{
var ID = db.Quess.Where(x => x.QueId == a).Select(i => i.Qued);
foreach (var j in ID)
{
var vv = db.Questio.Where(m => m.QId == j).ToList();
Quest.Add(vv);
}
}
}
}
}
You just need to use AddRange that accepts IEnumerable:
Quest.AddRange(vv);
Add is for adding a single item only.
If what you actually need is a list of lists, then declare Quest differently:
List<List<Questio>> Quest = new List<List<Questio>>();
...
var vv = db.Questio.Where(m => m.QId == j).ToList();
Quest.Add(vv);
Now your call to Add should work fine.
Two side notes, both beyond the scope of the question: 1) as the comments say, please review your naming; 2) this code could be simplified with more LINQ features, like Join and SelectMany.

Inserting value in List of values of a Key in Dictionary

I have a rowsDictionary that its keys point to a list of EmployeeSummary classes.
In those EmployeeSummary classes we also have a string property of Delivery_System
I am looping through this in this way but now stuck in the part that I want to have a deliverySystemFinder dictioanry that its keys are combinedKey as below and the value for each key is a list of distinct delivery_system values
//rowsDictionary is a Dictionary<string, List<EmployeeSummary>>
Dictionary<string, List<string>> deliverySystemFinder = new Dictionary<string, List<string>>();
foreach (string key in rowsDictionary.Keys)
{
List<EmployeeSummary> empList = rowsDictionary[key];
foreach (EmployeeSummary emp in empList)
{
string combinedKey = emp.LastName.Trim().ToUpper() + emp.FirstName.Trim().ToUpper();
string delivery_system = emp.Delivery_System;
// so now I should go and
//A) does deliverySystemFinder have this combinedKey? if not add it.
//B) Does combinedKey in the list of its values already have the value for delivery_system? if it does not then add it
}
}
This would work, for start:
foreach (string key in rowsDictionary.Keys)
{
List<EmployeeSummary> empList = rowsDictionary[key];
foreach (EmployeeSummary emp in empList)
{
string combinedKey = emp.LastName.Trim().ToUpper() +
emp.FirstName.Trim().ToUpper();
string delivery_system = emp.Delivery_System;
List<string> systems = null;
// check if the dictionary contains the list
if (!deliverySystemFinder.TryGetValue(combinedKey, out systems))
{
// if not, create it and add it
systems = new List<string>();
deliverySystemFinder[combinedKey] = systems;
}
// check if the list contains the value and add it
if (!systems.Contains(delivery_system))
systems.Add(delivery_system);
}
}
Now, a couple of remarks:
It doesn't make sense to iterate through Keys, and then do a lookup in each iteration. You can directly iterate KeyValuePairs using a foreach loop.
Using concatenated strings as unique keys often fails. In this case, what happens if you have users { LastName="Some", FirstName="Body" } and { LastName="So", FirstName="Mebody" } in your list?
Checking if a List contains a value is a O(n) operation. You would greatly improve performance if you used a HashSet<string> instead.
Finally, the simplest way to achieve what you're trying to do is to ditch those loops and simply use:
// returns a Dictionary<EmployeeSummary, List<string>>
// which maps each distinct EmployeeSummary into a list of
// distinct delivery systems
var groupByEmployee = rowsDictionary
.SelectMany(kvp => kvp.Value)
.GroupBy(s => s, new EmployeeSummaryEqualityComparer())
.ToDictionary(
s => s.Key,
s => s.Select(x => x.Delivery_System).Distinct().ToList());
With EmployeeSummaryEqualityComparer defined something like:
class EmployeeSummaryEqualityComparer : IEqualityComparer<EmployeeSummary>
{
public bool Equals(EmployeeSummary x, EmployeeSummary y)
{
if (object.ReferenceEquals(x, null))
return object.ReferenceEquals(y, null);
return
x.FirstName == y.FirstName &&
x.LastName == y.LastName &&
... (depending on what constitutes 'equal' for you)
}
public int GetHashCode(EmployeeSummary x)
{
unchecked
{
var h = 31; // null checks might not be necessary?
h = h * 7 + (x.FirstName != null ? x.FirstName.GetHashCode() : 0);
h = h * 7 + (x.LastName != null ? x.LastName.GetHashCode() : 0);
... other properties similarly ...
return h;
}
}
}
If you really think that using the string key will work in all your cases, you can do it without the custom equality comparer:
// returns a Dictionary<string, List<string>>
var groupByEmployee = rowsDictionary
.SelectMany(kvp => kvp.Value)
.GroupBy(s => s.LastName.ToUpper() + s.FirstName.ToUpper())
.ToDictionary(
s => s.Key,
s => s.Select(x => x.Delivery_System).Distinct().ToList());

Elegant way to check if a list contains an object where one property is the same, and replace only if the date of another property is later

I have a class as follows :
Object1{
int id;
DateTime time;
}
I have a list of Object1. I want to cycle through another list of Object1, search for an Object1 with the same ID and replace it in the first list if the time value is later than the time value in the list. If the item is not in the first list, then add it.
I'm sure there is an elegant way to do this, perhaps using linq? :
List<Object1> listOfNewestItems = new List<Object1>();
List<Object1> listToCycleThrough = MethodToReturnList();
foreach(Object1 object in listToCycleThrough){
if(listOfNewestItems.Contains(//object1 with same id as object))
{
//check date, replace if time property is > existing time property
} else {
listOfNewestItems.Add(object)
}
Obviously this is very messy (and that's without even doing the check of properties which is messier again...), is there a cleaner way to do this?
var finalList = list1.Concat(list2)
.GroupBy(x => x.id)
.Select(x => x.OrderByDescending(y=>y.time).First())
.ToList();
here is the full code to test
public class Object1
{
public int id;
public DateTime time;
}
List<Object1> list1 = new List<Object1>()
{
new Object1(){id=1,time=new DateTime(1991,1,1)},
new Object1(){id=2,time=new DateTime(1992,1,1)}
};
List<Object1> list2 = new List<Object1>()
{
new Object1(){id=1,time=new DateTime(2001,1,1)},
new Object1(){id=3,time=new DateTime(1993,1,1)}
};
and OUTPUT:
1 01.01.2001
2 01.01.1992
3 01.01.1993
This is how to check:
foreach(var object in listToCycleThrough)
{
var currentObject = listOfNewestItems
.SingleOrDefault(obj => obj.Id == object.Id);
if(currentObject != null)
{
if (currentObject.Time < object.Time)
currentObject.Time = object.Time
}
else
listOfNewestItems.Add(object)
}
But if you have large data, would be suggested to use Dictionary in newest list, time to look up will be O(1) instead of O(n)
You can use LINQ. Enumerable.Except to get the set difference(the newest), and join to find the newer objects.
var listOfNewestIDs = listOfNewestItems.Select(o => o.id);
var listToCycleIDs = listToCycleThrough.Select(o => o.id);
var newestIDs = listOfNewestIDs.Except(listToCycleIDs);
var newestObjects = from obj in listOfNewestItems
join objID in newestIDs on obj.id equals objID
select obj;
var updateObjects = from newObj in listOfNewestItems
join oldObj in listToCycleThrough on newObj.id equals oldObj.id
where newObj.time > oldObj.time
select new { oldObj, newObj };
foreach (var updObject in updateObjects)
updObject.oldObj.time = updObject.newObj.time;
listToCycleThrough.AddRange(newestObjects);
Note that you need to add using System.Linq;.
Here's a demo: http://ideone.com/2ASli
I'd create a Dictionary to lookup the index for an Id and use that
var newItems = new List<Object1> { ...
IList<Object1> itemsToUpdate = ...
var lookup = itemsToUpdate.
Select((i, o) => new { Key = o.id, Value = i }).
ToDictionary(i => i.Key, i => i.Value);
foreach (var newItem in newitems)
{
if (lookup.ContainsKey(newitem.ID))
{
var i = lookup[newItem.Id];
if (newItem.time > itemsToUpdate[i].time)
{
itemsToUpdate[i] = newItem;
}
}
else
{
itemsToUpdate.Add(newItem)
}
}
That way, you wouldn't need to reenumerate the list for each new item, you'd benefit for the hash lookup performance.
This should work however many times an Id is repeated in the list of new items.

LINQ IEnumerable and List

This function returns a dictionary with all key name values coming from an IEnumerable object. I want to filter out any items that do not exist in my list that I am passing in. I just want the properties that exist as a cols.Name
public static Dictionary<string, string> GetDataRowFromObject(IEnumerable<NameValue<string, object>> properties, List<ColDefModel> cols)
{
var dataRow = new Dictionary<string, string>();
foreach (NameValue<string, object> property in properties)
{
try
{
if (property.Value == null)
dataRow[property.Name] = "";
else
dataRow[property.Name] = property.Value.ToString();
}
catch (NullReferenceException e)
{
dataRow[property.Name] = "";
}
}
return dataRow;
}
var dictionary = properties.Where(nv => nv.Value != null)
.Where(nv => cols.Any(c => c.ColName == nv.Name))
.ToDictionary(nv => nv.Name, nv => nv.Value.ToString());
Hopefully this shows you why your method is unnecessary.
If I understand the question correctly, try doing your foreach through:
properties.Where(x => cols.Any(y => x.Name == y.Name))
instead of through properties. If performance is an issue, try making some sort of hashed list of the names in cols (like a HashSet); say it's called colNames, then change the above to x => colNames.ContainsKey(x.Name).

Categories