How to compare two lists and change one property - c#

I have a class:
public class Doc
{
public int Id {get; set;}
public string Name {get; set;}
public bool IsActive {get; set;}
}
And two lists of Doc type.
How to write LINQ to compare these list and change IsActive property of first list if contains id from second list.

Given that your target list is named targetDocs and the list you want to check for document existance is srcDocs try something like (don't have access to a compiler here so can't test):
targetDocs.ForEach(d => d.IsActive = srcDocs.Any(sd => sd.id == d.Id))
I'm assuming that we are talking about Lists and not other collection types as the ForEach extension method is defined for Lists.

It's better to use the HashSet<T> collection for such operations, it has fast O(1) lookups and there is no real reason to use LINQ for changing property values here. True, we have to create another collection here and it takes time to allocate resources initialize it etc. but if there is one million records it will give huge performance boost.
Provided that docs1 is collection where you would like to change IsActive to true if docs2 collection has Id, you can use :
var ids = new HashSet<int>(docs2.Select(d => d.Id));
foreach(var doc in docs1)
{
// .Contains implementation in HashSet has fast O(1) lookups
doc.IsActive = ids.Contains(doc.Id);
}

You can create your own static method which is responsible for checking active property. I've used enumerators here, it works fast and easy to understand.
static class Program
{
static void Main(string[] args)
{
List<Data> firstInstance = new List<Data>
{
new Data { Id = 1, IsActive = false },
new Data { Id = 2, IsActive = false }
};
List<Data> secondInstance = new List<Data>
{
new Data { Id = 1, IsActive = false },
new Data { Id = 3, IsActive = false }
};
firstInstance.CheckActive(secondInstance);
}
static void CheckActive(this List<Data> firstInstance, List<Data> secondInstance)
{
using (IEnumerator<Data> firstEnumerator = firstInstance.GetEnumerator(), secondEnumerator = secondInstance.GetEnumerator())
{
while (true)
{
if (!firstEnumerator.MoveNext() || !secondEnumerator.MoveNext()) break;
if (firstEnumerator.Current.Id == secondEnumerator.Current.Id) firstEnumerator.Current.IsActive = true;
}
}
}
}
class Data
{
public int Id { get; set; }
public bool IsActive { get; set; }
}

You wrote:
How to write linq to compare these list and change "IsActive" propety of first list if contains id from second list.
Your first list does not have an IsActive property, however the elements of your list have one. Therefore I assume you want the following:
How do I get a sequence of all elements in the first list that have an Id of any of the elements in the second list, so I can change the IsActive properties of those elements.
LINQ is used to enumerate over objects. You can't change the object in the linq statement
If you want to change the objects, you'll have to enumerate over them, for instance with ForEach.
IEnumerable<int> activeIds = secondList.Select(item => item.Id);
IEnumerable<Doc> activeDocs = firstList.Where(item => activedIds.Contains(item.Id));
foreach (Doc activeDoc in activeDocs)
{
activeDoc.IsActive = true;
}
Beware: I did not change ISActive for all inActive docs. If you want that you'll have to foreach all elements from your firstList:
IEnumerable<int> activeIds = secondList.Select(item => item.Id);
foreach (Doc doc in firstList)
{
doc.IsActive = activeIds.Contains(doc.Id);
}

You can use this solution without using linq
foreach(Doc x in list1){
foreach(Doc y in list2){
if(x.id == y.id){
y.IsActive = true;
}
}
}

Related

C# - LINQ - Sorting collection by double-nested properties

Let's assume that we have a few classes:
public class SecondNestingLevel
{
public string SortPropety {get; set;}
}
public class FirstNestingLevel
{
public ICollection<SecondNestingLevel> SecondNestingLevelCollection {get; set;}
}
public class Wrapper
{
public ICollection<FirstNestingLevel> FirstNestingLevelCollection {get; set;}
}
I have to sort collection of Wrapper objects by SortPropety (located inside double-nested SecondNestingLevel objects).
Guidelines:
Firstly, aggregate together Wrapper objects, which every value of SortPropety is the same (in every FirstNestingLevel and SecondNestingLevel located inside specific Wrapper object).
Secondly, sort aggregated in this way Wrapper objects in a alphabetic way.
General schema of result collection:
Part 1: Wrappers with the same value of SortProperty sorted in an alphabetic way.
Part 2: Other wrappers sorted in an alphabetic way of SortProperty located in first SecondNestingLevel object which is placed in first FirstNestingLevel object in nested collections.
I would be very grateful for your help.
To order SortProperty I made this example:
var wrapper = new Wrapper()
{
FirstNestingLevelCollection = new List<FirstNestingLevel>()
{
new FirstNestingLevel() { SecondNestingLevelCollection = new List<SecondNestingLevel>()
{
new SecondNestingLevel() { SortPropety = "11" },
new SecondNestingLevel() { SortPropety = "02" },
new SecondNestingLevel() { SortPropety = "05" }
}
}
}
};
Now to order SortPropety you need to go through each element in firstNestingLevel to be able to order SecondNestingLevel.
foreach (var firstNestingLevel in wrapper.FirstNestingLevelCollection)
{
var orderedEnumerable = firstNestingLevel
.SecondNestingLevelCollection.OrderBy(e => e.SortPropety);
foreach (var secondNestingLevel in orderedEnumerable)
{
Console.WriteLine(secondNestingLevel.SortPropety);
}
}
If you need it in Linq, one way to go is using Select and you will get a nested loop of ordered elements:
var orderedEnumerable = wrapper.FirstNestingLevelCollection
.Select(e => e.SecondNestingLevelCollection.OrderBy(e1 => e1.SortPropety));
To print that you can do something like:
foreach (var secondNestingLevels in orderedEnumerable)
{
foreach (var secondNestingLevel in secondNestingLevels)
{
Console.WriteLine(secondNestingLevel.SortPropety);
}
}
Both codes will give the same output:
02
05
11

Trying to use reflection to concatenate lists of objects

I have below class
public class HydronicEquipment
{
public List<LibraryHydronicEquipment> Source { get; set; }
public List<LibraryHydronicEquipment> Distribution { get; set; }
public List<LibraryHydronicEquipment> Terminals { get; set; }
}
and then i have the below class for "libraryHydronicEquipment"
public class LibraryHydronicEquipment : IEquipmentRedundancy
{
public string Name { get; set; }
public RedundancyStatus RedundancyStatus { get; set; }
public EquipmentRedundancy EquipmentRedundancy { get; set; }
}
I am trying to concatenate the list of "LibraryHydronicEquipment" objects available from all three properties (i.e) from source, distribution and terminal and General concatenate method will looks like as this below
var source = hydronicEquipment.Source;
var distribution = hydronicEquipment.Distribution;
var teriminals = hydronicEquipment.Terminals;
Source.Concat(Distribution).Concat(Terminals)
I am trying to achieve the same using reflection and the code looks like as below
foreach (var (systemName, hydronicEquipment) in hydronicSystemEquipment)
{
bool isFirstSystem = true;
var equipmentList = new List<string> { "Source", "Distribution", "Terminals" };
var redundancyequipmentList = GetRedundancyEquipment(hydronicEquipment, equipmentList);
}
and the method GetRedundancyEquipment is looks like below
private static IEnumerable<IEquipmentRedundancy> GetRedundancyEquipment(HydronicEquipment hydronicEquipment, List<string> equipmentList)
{
IEnumerable<IEquipmentRedundancy> equipmentRedundancies = new List<IEquipmentRedundancy>();
dynamic equipmentResults = null;
foreach(var equipment in equipmentList)
{
var componentList = hydronicEquipment.GetType().GetProperty(equipment).GetValue(hydronicEquipment, null) as IEnumerable<IEquipmentRedundancy>;
equipmentResults = equipmentRedundancies.Concat(componentList);
}
return equipmentResults;
}
The problem here is even though i have Source is having list of objects and Distribution is having list of objects, the equipmentResults is giving only one object instead of list of concatenated objects.
I am trying to return the IEnumerable<IEquipmentRedundancy> at the end using reflection method but it seems not working with the above code.
Could any one please let me know how can i achieve this, Many thanks in advance.
GetRedundancyEquipment should preserve your values instead of reassign the reference with each iteration. Here's the fixed version:
private static IEnumerable<IEquipmentRedundancy> GetRedundancyEquipment(HydronicEquipment hydronicEquipment, List<string> equipmentList)
{
IEnumerable<IEquipmentRedundancy> equipmentRedundancies = new List<IEquipmentRedundancy>();
var equipmentResults = new List<IEquipmentRedundancy>();
foreach (var equipment in equipmentList)
{
var componentList = hydronicEquipment.GetType().GetProperty(equipment).GetValue(hydronicEquipment, null) as IEnumerable<IEquipmentRedundancy>;
equipmentResults.AddRange(equipmentRedundancies.Concat(componentList));
}
return equipmentResults;
}
If we look at what you're doing in GetRedundancyEquipment() it becomes clear.
First you create equipmentRedundancies = new List<IEquipmentRedundancy>();
Then you never modify equipmentRedundancies - e.g. via Add(). It remains an empty list until it goes out of scope and is garbage collected.
In a loop you then repeatedly make this assignment equipmentResults = equipmentRedundancies.Concat(componentList);
That is to say: Assign to equipmentResults the concatenation of componentList to equipmentRedundancies.
Note that Concat() is a lazily evaluated linq method. When you actually enumerate it results are produced. It doesn't modify anything, it's more like a description of how to produce a sequence.
So each time through the loop you're assigning a new IEnumerable that describes a concatentaion of an empty list followed by the property that you retrieved with reflection to equipmentResults. Then at the end you return the final one of these concatenations of an empty list and retrieved property.
If you want all of them together, you should concatenate each of them to the result of the previous concatenation, not to an empty list.

Fastest way of comparing two lists without using nested loops

I have two types:
public class SubCategories
{
public static List<SubCategories> subCategories = new List<SubCategories>();
public string title { get; set; }
public string IDfromCategories { get; set; }
public string subCategoryID { get; set; }
public bool isChecked { get; set; }
}
public class UserInsideCategories
{
public string userEmail { get; set; }
public string iDfromSubCategories { get; set; }
}
And two lists both containing this object multiple times.
Now I wanna go through a list with type SubCategories and check each object, if it contains the same value as my other list of type UserInsideCategories. Specifically, I wanna know if any object on the list.SubcategoryID is equal to any object on the other list.IdFromSubCateogires.
I achieved this like so:
List<SubCategories> branch = new List<SubCategories>();
for(int i = 0; i < subCategories.Count; i++)
{
SubCategories e = new SubCategories();
for(int x = 0; x < allSubs.Count; x++)
{
if (e.IDfromCategories == allSubs[x].iDfromSubCategories)
e.isChecked = true;
}
branch.Add(e);
}
So I am using a nested loop. But since I have to do this multiple times, it takes far too long.
I also thought about turning all values from SubCategories into a simple string array and use the Contains function, to see if the current object.IDfromCategories contains the object on the array. This would mean I would NOT use a for loop. But interenally, I believe, the system is still using a loop and therefore there would be no performance benefit.
What would be the best way of checking each object if it contains a value from the other list?
You should use some kind of lookup table. Probably either HashSet or Dictionary. The former only allows checking if a key exists in the set, while the later allows you to also find the object the key belongs to.
To check all the UserInsideCategories that shares an id with a SubCategories you could write:
var dict = subCategoriesList.ToDictionary(s => s.subCategoryID, s => s);
var matches = userInsideCategoriesList.Where(l => dict.ContainsKey(l.iDfromSubCategories));
if you want matching pairs you could write:
foreach (var user in userInsideCategoriesList)
{
if (dict.TryGetValue(user.iDfromSubCategories, out var subCategory))
{
// Handle matched pairs
}
}
This assumes that the ID is unique in respective list. If you have duplicates you would need something like a multi-value dictionary. There are no multi-value dictionary built in, but I would expect there are some implementations if you search around a bit.

Can't change value in foreach from IEnumerable<Model>

How to change value of an object in foreach from IEnumerable<Model>.
Code:
public IEnumerable<Model> ListDaftarPjsp()
{
IEnumerable<Model> list = from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar
};
foreach (Model item in list) {
item.condition = "example";
}
return list;
}
public class Model{
public string foo{ get; set; }
public string bar { get; set; }
public string condition{ get; set; }
}
I already create Model. Then I am looping result using foreach, then set it. But the Return for conditionstill not changing? how to set condition inside foreach then return it for result
IEnumerable<T> is a query, not a collection. While there is some sort of collection at the other end, the query itself is not the collection. The nature of the collection you are targeting will determine whether or not you can modify the contents.
The general rule of thumb is that you can't expect an IEnumerable<T> to return the same list of objects twice, or even expect that you will be able to enumerate across it more than once - it is perfectly valid (if unusual) for an IEnumerable<T> to enumerate once only and refuse to enumerate a second or third time.
In this case what you have is actually a database query of type IQueryable<Model> that is cast to IEnumerable<Model>. It's still an IQueryable<Model> which means that each time you enumerate across it you will get (probably) the same list of data but in completely new objects. Changing one of the objects won't change all of the objects for the same source record, nor change the contents of the underlying record itself.
If you are trying to modify the returned objects without changing the underlying records (seems to be the case) then you need to materialize the query into a collection in memory. There are a few ways to do this depending on what you're expecting to do with the returned data.
The simplest is to convert the query to an array using the .ToArray() extension method:
public Model[] ListDaftarPjsp()
{
var query = from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar
};
var list = query.ToArray();
foreach (Model item in list)
{
item.condition = "example";
}
return list;
}
Now the records are in an array in memory and enumeration of that array can be done multiple times returning the same exact objects instead of fetching new copies of the data from the database every time.
Here you are trying to create a list of Model using LINQ, then you are iterating the same for adding an additional property to each item. Then why don't you add the property at the time of creation of the list instead for an additional loop? Make things simple by try something like this:
from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar,
condition = GetCondition(x.Foo)
};
Where the GetCondition() can be defined as :
private string GetCondition(int foo)
{
if(item.foo == 1)
{
return "a";
}
else if(item.foo == 2)
{
return "b";
}
else
{
return "xx";
}
}
There is already for this topic but there is more efficient way to do this.
Just use List<> instead of Array[].
public List<Model> ListDaftarPjsp()
{
List<Model> list = from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar
};
foreach (Model item in list)
{
item.condition = "example";
}
return list;
}
public class Model{
public string foo{ get; set; }
public string bar { get; set; }
public string condition{ get; set; }
}
I case you dont want to load items in memory with a .ToArray or .ToList
You can use .Select from Linq.
return myEnumeration.Select(item => {
item.condition = "example";
return item;
})

LINQ select one field from list of DTO objects to array

I have DTO class that defines order line like this:
public class Line
{
public string Sku { get; set; }
public int Qty { get; set; }
}
A list of type Line is populated like so:
List<Line> myLines = new List<Line>();
myLines.Add(new Line() { Sku = "ABCD1", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD2", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD3", Qty = 1 });
What I want is to use LINQ to get an array of SKUs from the myLines List. How can I go about doing that?
I am currently doing it manually like this ...
// Get SKU List
List<string> mySKUs = new List<string>();
foreach (Line myLine in myLines)
mySKUs.Add(myLine.Sku);
string[] mySKUsArray = mySKUs.ToArray();
I was trying to google for a solution, but I wasn't sure how to word the question...
P.S. is there any benefit/performance gain in using LINQ method to achieve what I am currently doing with foreach?
You can use:
var mySKUs = myLines.Select(l => l.Sku).ToList();
The Select method, in this case, performs a mapping from IEnumerable<Line> to IEnumerable<string> (the SKU), then ToList() converts it to a List<string>.
Note that this requires using System.Linq; to be at the top of your .cs file.
This is very simple in LinQ... You can use the select statement to get an Enumerable of properties of the objects.
var mySkus = myLines.Select(x => x.Sku);
Or if you want it as an Array just do...
var mySkus = myLines.Select(x => x.Sku).ToArray();
I think you're looking for;
string[] skus = myLines.Select(x => x.Sku).ToArray();
However, if you're going to iterate over the sku's in subsequent code I recommend not using the ToArray() bit as it forces the queries execution prematurely and makes the applications performance worse. Instead you can just do;
var skus = myLines.Select(x => x.Sku); // produce IEnumerable<string>
foreach (string sku in skus) // forces execution of the query
You can select all Sku elements of your myLines list and then convert the result to an array.
string[] mySKUsArray = myLines.Select(x=>x.Sku).ToArray();
In the case you're interested in extremely minor, almost immeasurable performance increases, add a constructor to your Line class, giving you such:
public class Line
{
public Line(string sku, int qty)
{
this.Sku = sku;
this.Qty = qty;
}
public string Sku { get; set; }
public int Qty { get; set; }
}
Then create a specialized collection class based on List<Line> with one new method, Add:
public class LineList : List<Line>
{
public void Add(string sku, int qty)
{
this.Add(new Line(sku, qty));
}
}
Then the code which populates your list gets a bit less verbose by using a collection initializer:
LineList myLines = new LineList
{
{ "ABCD1", 1 },
{ "ABCD2", 1 },
{ "ABCD3", 1 }
};
And, of course, as the other answers state, it's trivial to extract the SKUs into a string array with LINQ:
string[] mySKUsArray = myLines.Select(myLine => myLine.Sku).ToArray();

Categories