Need help joining two lists of objects in C# - c#

With my two lists of objects, I want to keep the total set of unique items based on a string key, where any collisions come from the first list, and any misses come from the second. Stated differently, I want to ignore any items in the first list that are not in the second list, but I want to keep all items that do exist in the second list as well as any remaining items from the second list.
What's the best way to do this?
Edit: This problem is more subtle than a simple union. A union will join the distinct items from two lists. In the case of a collision it takes the item from the outer list.
In my case, I have some items in List1 that I don't want to keep because they don't exist in List2, while I do want to keep all items from list 2.
Is there a cleaner / shorter way to do the below?
var remaining = allowedItems.Except(recentItems)
var allowedRecentItems = recentItems.Intersect(allowedItems)
var result = allowedRecentItems.Concat(remaining);

Try this:
var resultlist = list1.Union(list2);

var list1 = new List<string>{"A", "B", "C"};
var list2 = new List<string>{"B", "C", "D"};
var list = list1.Union(list2);

If I understood it correctly - You should keep the second list only. By that your both conditions are fulfilled
I want to ignore any items in the first list that are not in the second list
off-course if you keep the second list only then all the items in the first list which are not present in the second list will automatically be ignored.
I want to keep all items that do exist in the second list as well as any remaining items from the second list
On the other hand, the items present in both of the lists will automatically be selected including those which are only present in the second list.
If that's not what you want then you should check List.Distinct(), List.Except() and List.Union() extension methods.

Using the second list just do the trick. Directly translating your your requirements produces:
list1.Intersect(list2).Union(list2) which results list2

Related

c# how to check if two Lists of SelectedListItem have the same values

I am struggling with the basic thing. I am trying to compare two lists of SelectedListItem. I would like to know if the two lists have the same values.
Let's imagine that we have
List<SelectedListItem> listA, listB
I already tried many ways like:
new HashSet<SelectListItem>( listA ).SetEquals( listB );
or
var firstNotSecond = listA .Except( listB ).ToList();
var secondNotFirst = listB .Except( listA ).ToList();
or
listA.Contains(listB[i])
or even simple
listA==listB
I am still getting the false output, but I am 100% sure that these values are the same since one list is created from the second one.
Is there a simple way to compare their items?
Thanks a lot
EDIT
Context:
I created a listA from one database and put it into another empty database. Now I downloaded all the data from the second database as listB and I am trying to check if the data were changed
You have to compare the values to compare the references will not work so maybe write a small helper method to check if two SelectedListItems have the same values and check if both lists have the same items.
If you want to do it the pretty way you could e.g. implement the IEqualityComparer<T> interface for the SelectedListItem class
If it is a quick and dirty single time check and the size of the tables isn't that big you could also serialize it and compare the output directly

How to filter a list by another list

I have two lists :
List<myObject> mainList;
And
List<myObject> blackList;
I'm trying to make a new list by a condition and another condition that the elements should not be in the blacklist.
Here is my attempt :
List<myObject> newList = mainList.Where(x => x.Id == 5 && !blackList.Contains(x)).ToList();
This newList is generated inside a loop, in the first round of the loop, blackList is empty and it works, in the second round blackList contains about 200k elements. And when the above line works, it doesn't move next, it stays there for minutes. How can I do filtering more efficiently so that I wouldn't get elements which are in the blackList? Thanks.
The problem you're facing is due to the way List<T> implements Contains - it searches linearly through which is quite slow and inefficient for long lists.
To get better performance you could use a better structure for the blacklist - one with a faster/better implementation for long lists like a HashSet<T>
var blackList = new HashSet<myObject>(theBlackList);

C# List<string> contains partial match from another List<string>

I have these lists:
var list1 = new List<string>
{
"BOM_Add",
"BOM_Edit",
"BOM_Delete",
"Paper_Add",
"Paper_Edit",
"Paper_Delete"
};
var list2 = new List<string> {"BOM", "Paper_Add"};
I want to create a third list of the common items based on a partial match. So, the third list should contain:
"BOM_Add",
"BOM_Edit",
"BOM_Delete",
"Paper_Add"
because the second list contains "BOM".
If the second list contained "_Edit", then I would expect the third list to have
"BOM_Edit",
"Paper_Edit"
I know how to do this with .Intersect() if I spell out each item (e.g. "BOM_Add") in the second list, but I need it to be more flexible than that.
Can this be done without iterating through each item on the first list? These lists may get very long and I would prefer to avoid that if I can.
You can use LINQ
var result = list1.Where(r => list2.Any(t => r.Contains(t)))
.ToList();
For output:
foreach (var item in result)
{
Console.WriteLine(item);
}
Output would be:
BOM_Add
BOM_Edit
BOM_Delete
Paper_Add
Can this be done without iterating through each item on the first
list?
You have to iterate, either through a loop or using LINQ (which internally iterates as well)
Can this be done without iterating through each item on the first list?
No, if you want to find all of the items that contain one of the items that you have. There is no way of building an index, or any sort of structure that can rule out large sections of items without checking each one. The only option is to compare every single item in the first list with every single item in the other list, doing your Contains check.
If you only needed to do a StartsWith instead of a Contains, then you could sort your list, do a BinarySearch to find the item nearest to the item that you're searching for, which would allow you to easily find all of the items that start with a particular string while only actually needing to check O(log(n) + m) items (where n is the size of the list an m is the average number of matches). You could do the same thing with an EndsWith too, if you just sorted items based on the reverse of the string, but there's no way to sort an items such that a Contains check does this.

What's the best way to normalize between two lists using Linq?

Given two IEnumerable instances in C# (call them a and b), what's the best way to remove all values in a that are not in b and add all values of b that are not in a?
I know I could just set a = b, normally, but this is ultimately for persisting to the DB via Entity Framework CodeFirst in an MVC application so there's some wonkiness of state to watch out for. In fact, we're talking about updating a record based on stuff posted from the client.
The closest that seems to work involves about for foreach loops, one to iterate the 'a' list and populate a collection of 'items to be removed', another to iterate the 'b' list to identify the 'items to be added', and then one each on the 'items to be removed' and 'items to be added' collections to add and remove items, respectively (since you can't modify the 'a' collection while you're iterating on it.
That feels clunky, though; is there a better way?
UPDATE
For clarity, I'll make an example. Let's say I have an entity I fetch from the DB which represents a blog Post (since that example never gets tired...) and said Post has a collection of Tags. From the client, I get a list of Tags that should be now the 'canonical' list of tags, but none of them are entities, it's just an in-memory collection. What I want to do is ensure that Post.Tags matches the tags being posted by the client, without creating duplicate tags in the database.
You can use Intersect, Concat and Except:
a = a.Intersect(b).Concat(b.Except(a));
Intersect returns items that exist in both collections, so a.Intersect(b) will give you all items that are in a and b.
Except returns elements that are in first collection, but not in the other, so b.Except(a) returns elements that are in b but not in a.
Concat concatenates these two collections.
But I don't really get your questions, so I'm not sure it's what you're looking for.
It sounds like your enumerable a is actually a list, so I did it this way:
var a = new List<int>() { 1, 2, 3, };
var b = new List<int>() { 1, 3, 4, 5, };
foreach (var x in a.Except(b).ToArray())
{
a.Remove(x);
}
foreach (var x in b.Except(a).ToArray())
{
a.Add(x);
}
At the end a has the same elements as b.
However, you need to be careful if you have duplicates in b.

Can i Union 3 ToSelectList

I have three seperate ToSelectList items and i wanted to combine each list into one dropdown list box and was wondering if i could use a Union for it. Or is it that a union is only for 2 toselectlist items only.
Thanks!
When item exists in more than one list, do you want item to appear once, or as many times as they exists in all lists?
As I understand you want just concat:
var combinedList = list1.Concat(list2).Concat(list3).ToList();
If you want to avoid duplicates:
var unionList = list1.Union(list2).Union(list3).ToList();
Union is more expensive, as it has to go through the list and take care of duplicates.
If your item is a reference type and there is no IComparable or IEquable interfaces and you don't provide IEqualityComparer, you likely don't need Union.
You can only union two lists at a time using Union() - but you can chain it to achieve what you want:
var resultList = list1.Union(list2).Union(list3).ToList();
You can try using the Union() linq extension method.
var finalSelectList = model.getFirstList().ToSelectList().Union(
model.getSecondList().ToSelectList().Union(
model.getThirdList().ToSelectList()))

Categories