If at least one element of list1 is in list2? - c#

Having the following:
public List<int> List1 { get; set; }
...
var x = GiveMeObject(); // x.List2 --> each element on list2 has an Id (int).
...
bool containsAtLeastOne = ???
What is the easiest/fastest/shortest way (in linq) to verify if at least 1 element of list1 is in the list2 ?
Thanks

bool containsAtLeastOne = x.List2.Any(li => List1.Contains(li.Id));

alternative: Intersect
bool containsAtLeastOne = List1.Intersect(x.List2.Select(e => e.Id)).Any()
If your collections are getting large, you should use Intersect instead of Contains, since Intersect is at least as fast as Contains. Depending on your collecions, Contains can get slow quickly.
If your collections are quite small (< 1000 elements), this difference would probably not matter.
If you don't mind a non-LINQ way and some more lines of code, you could use
var tmp = new HashSet<int>(x.List2.Select(e => e.ID));
tmp.IntersectWith(list1);
bool containsAtLeastOne = tmp.Any();
which will probably be faster than the LINQ approach.

Related

Update a property field in a List

I have a List<Map> and I wanted to update the Map.Target property based from a matching value from another List<Map>.
Basically, the logic is:
If mapsList1.Name is equal to mapsList2.Name
Then mapsList1.Target = mapsList2.Name
The structure of the Map class looks like this:
public class Map {
public Guid Id { get; set; }
public string Name { get; set; }
public string Target { get; set; }
}
I tried the following but obviously it's not working:
List<Map> mapsList1 = new List<Map>();
List<Map> mapsList2 = new List<Map>();
// populate the 2 lists here
mapsList1.Where(m1 => mapsList2.Where(m2 => m1.Name == m2.Name) ) // don't know what to do next
The count of items in list 1 will be always greater than or equal to the count of items in list 2. No duplicates in both lists.
Assuming there are a small number of items in the lists and only one item in list 1 that matches:
list2.ForEach(l2m => list1.First(l1m => l1m.Name == l2m.Name).Target = l2m.Target);
If there are more than one item in List1 that must be updated, enumerate the entire list1 doing a First on list2.
list1.ForEach(l1m => l1m.Target = list2.FirstOrDefault(l2m => l1.Name == l2m.Name)?.Target ?? l1m.Target);
If there are a large number of items in list2, turn it into a dictionary
var d = list2.ToDictionary(m => m.Name);
list1.ForEach(m => m.Target = d.ContainsKey(m.Name) ? d[m.Name].Target : m.Target);
(Presumably list2 doesn't contain any repeated names)
If list1's names are unique and everything in list2 is in list1, you could even turn list1 into a dictionary and enumerate list2:
var d=list1.ToDictionary(m => m.Name);
list2.ForEach(m => d[m.Name].Target = m.Target);
If List 2 has entries that are not in list1 or list1 has duplicate names, you could use a Lookup instead, you'd just have to do something to avoid a "collection was modified; enumeration may not execute" you'd get if you were trying to modify the list it returns in response to a name
mapsList1.Where(m1 => mapsList2.Where(m2 => m1.Name == m2.Name) ) // don't know what to do next
LINQ Where doesn't really work like that / that's not a statement in itself. The m1 is the entry from list1, and the inner Where would produce an enumerable of list 2 items, but it doesn't result in the Boolean the outer Where is expecting, nor can you do anything to either of the sequences because LINQ operations are not supposed to have side effects. The only thing you can do with a Where is capture or use the sequence it returns in some other operation (like enumerating it), so Where isn't really something you'd use for this operation unless you use it to find all the objects you need to alter. It's probably worth pointing out that ForEach is a list thing, not a LINQ thing, and is basically just another way of writing foreach(var item in someList)
If collections are big enough better approach would be to create a dictionary to lookup the targets:
List<Map> mapsList1 = new List<Map>();
List<Map> mapsList2 = new List<Map>();
var dict = mapsList2
.GroupBy(map => map.Name)
.ToDictionary(maps => maps.Key, maps => maps.First().Target);
foreach (var map in mapsList1)
{
if (dict.TryGetValue(map.Name, out var target))
{
map.Target = target;
}
}
Note, that this will discard any possible name duplicates from mapsList2.

Lambda Expressions, Compare item1, if they are equal compare item2

I tried to search but I cannot seem to find my answer. I think an answer may exist as it not a uncommon question. I trying to say Sort by Item1. If they are equal, sort by Item2
sorted.Sort((a,b)=>(a.Item1.CompareTo(b.Item1)));
While you can build a comparer to do this with List<T>.Sort, it's much easier to use LINQ, which is built for this sort of thing:
sorted = unsorted.OrderBy(x => x.Item1).ThenBy(x => x.Item2).ToList();
If you really want to use Sort, you can use the ProjectionEqualityComparer in my MiscUtil project - but it won't be as nice as the LINQ approach.
var sorted = original.OrderBy(c => c.Item1).ThenBy(n => n.Item2).ToList()
Try this
As an alternative to the LINQ methods, you can create a comparer:
class FrobComparer : IComparer<Frob>
{
public int Compare(Frob x, Frob y)
{
int item1Comparison = x.Item1.CompareTo(y.Item1);
if (item1Comparison == 0)
return x.Item2.CompareTo(y.Item2);
return item1Comparison;
}
}
And then pass that into Sort(), assuming unsorted is a List<Frob>:
var sorted = unsorted.Sort(new FrobComparer());

Sort in-memory list by another in-memory list

Is possible to sort an in-memory list by another list (the second list would be a reference data-source or something like this) ?
public class DataItem
{
public string Name { get; set; }
public string Path { get; set; }
}
// a list of Data Items, randomly sorted
List<DataItem> dataItems = GetDataItems();
// the sort order data source with the paths in the correct order
IEnumerable<string> sortOrder = new List<string> {
"A",
"A.A1",
"A.A2",
"A.B1"
};
// is there a way to tell linq to sort the in-memory list of objects
// by the sortOrder "data source"
dataItems = dataItems.OrderBy(p => p.Path == sortOrder).ToList();
First, lets assign an index to each item in sortOrder:
var sortOrderWithIndices = sortOrder.Select((x, i) => new { path = x, index = i });
Next, we join the two lists and sort:
var dataItemsOrdered =
from d in dataItems
join x in sortOrderWithIndices on d.Path equals x.path //pull index by path
orderby x.index //order by index
select d;
This is how you'd do it in SQL as well.
Here is an alternative (and I argue more efficient) approach to the one accepted as answer.
List<DataItem> dataItems = GetDataItems();
IDictionary<string, int> sortOrder = new Dictionary<string, int>()
{
{"A", int.MaxValue},
{"A.A1", int.MaxValue-1},
{"A.A2", int.MaxValue -2},
{"A.B1", int.MaxValue-3},
};
dataItems.Sort((di1, di2) => sortOrder[di1.Path].CompareTo(sortOrder[di2.Path]));
Let's say Sort() and OrderBy() both take O(n*logn), where n is number of items in dataItems. The solution given here takes O(n*logn) to perform the sort. We assume the step required to create the dictionary sortOrder has a cost not significantly different from creating the IEnumerable in the original post.
Doing a join and then sorting the collection, however adds an additional cost O(nm) where m is number of elements in sortOrder. Thus the total time complexity for that solution comes to O(nm + nlogn).
In theory, the approach using join may boil down to O(n * (m + logn)) ~= O(n*logn) any way. But in practice, join is costing extra cycles. This is in addition to possible extra space complexity incurred in the linq approach where auxiliary collections might have been created in order to process the linq query.
If your list of paths is large, you would be better off performing your lookups against a dictionary:
var sortValues = sortOrder.Select((p, i) => new { Path = p, Value = i })
.ToDictionary(x => x.Path, x => x.Value);
dataItems = dataItems.OrderBy(di => sortValues[di.Path]).ToList();
custom ordering is done by using a custom comparer (an implementation of the IComparer interface) that is passed as the second argument to the OrderBy method.

How do I use Linq with a HashSet of Integers to pull multiple items from a list of Objects?

I have a HashSet of ID numbers, stored as integers:
HashSet<int> IDList; // Assume that this is created with a new statement in the constructor.
I have a SortedList of objects, indexed by the integers found in the HashSet:
SortedList<int,myClass> masterListOfMyClass;
I want to use the HashSet to create a List as a subset of the masterListOfMyclass.
After wasting all day trying to figure out the Linq query, I eventually gave up and wrote the following, which works:
public List<myclass> SubSet {
get {
List<myClass> xList = new List<myClass>();
foreach (int x in IDList) {
if (masterListOfMyClass.ContainsKey(x)) {
xList.Add(masterListOfMyClass[x]);
}
}
return xList;
}
private set { }
}
So, I have two questions here:
What is the appropriate Linq query? I'm finding Linq extremely frustrating to try to figuere out. Just when I think I've got it, it turns around and "goes on strike".
Is a Linq query any better -- or worse -- than what I have written here?
var xList = IDList
.Where(masterListOfMyClass.ContainsKey)
.Select(x => masterListOfMyClass[x])
.ToList();
If your lists both have equally large numbers of items, you may wish to consider inverting the query (i.e. iterate through masterListOfMyClass and query IDList) since a HashSet is faster for random queries.
Edit:
It's less neat, but you could save a lookup into masterListOfMyClass with the following query, which would be a bit faster:
var xList = IDList
.Select(x => { myClass y; masterListOfMyClass.TryGetValue(x, out y); return y; })
.Where(x => x != null)
.ToList();
foreach (int x in IDList.Where(x => masterListOfMyClass.ContainsKey(x)))
{
xList.Add(masterListOfMyClass[x]);
}
This is the appropriate linq query for your loop.
Here the linq query will not effective in my point of view..
Here is the Linq expression:
List<myClass> xList = masterListOfMyClass
.Where(x => IDList.Contains(x.Key))
.Select(x => x.Value).ToList();
There is no big difference in the performance in such a small example, Linq is slower in general, it actually uses iterations under the hood too. The thing you get with ling is, imho, clearer code and the execution is defered until it is needed. Not i my example though, when I call .ToList().
Another option would be (which is intentionally the same as Sankarann's first answer)
return (
from x in IDList
where masterListOfMyClass.ContainsKey(x)
select masterListOfMyClass[x]
).ToList();
However, are you sure you want a List to be returned? Usually, when working with IEnumerable<> you should chain your calls using IEnumerable<> until the point where you actually need the data. There you can decide to e.g. loop once (use the iterator) or actually pull the data in some sort of cache using the ToList(), ToArray() etc. methods.
Also, exposing a List<> to the public implies that modifying this list has an impact on the calling class. I would leave it to the user of the property to decide to make a local copy or continue using the IEnumerable<>.
Second, as your private setter is empty, setting the 'SubSet' has no impact on the functionality. This again is confusing and I would avoid it.
An alternate (an maybe less confusing) declaration of your property might look like this
public IEnumerable<myclass> SubSet {
get {
return from x in IDList
where masterListOfMyClass.ContainsKey(x)
select masterListOfMyClass[x]
}
}

How to get a list of Ints via a LINQ statement?

I have the following class
public class PaymentItemViewModel
{
public List<int> Placements { get; set; }
public int StandardPayment { get; set; }
}
Then a function returns the following
IEnumerable<PaymentItemViewModel> paymentItems
How can I get a list of all the Placements Ids, using LINQ?
Can i do something like the following?
List<int> placementIds = paymentItems.Any(x=>x.Placements) ??
Probably you are looking for Enumerable.SelectMany method
List<int> placementIds = paymentItems.SelectMany(vm => vm.Placements).ToList()
You can do this:
var placementIds = new List<int>();
foreach(var item in paymentItems)
{
foreach(var placementId in item.Placements)
{
placementIds.Add(placementId);
}
}
If you really want to do it in LINQ, then you can do this:
var placementIds = paymentItems.SelectMany(item => item.Placements).ToList();
This should work. Any() just sees if any exist, and takes a boolean. SelectMany grabs all of a property or object "and flattens the resulting sequences into one sequence".
List<int> placementIds = paymentItems.SelectMany(x=>x.Placements).ToList();
from aPaymentModelView in PaymentItems
from aPlacements in aPaymentmodelView
select aPlacements
this should work according to this
Flatten List in LINQ
EDIT
nicely provided by hazzik
var ids1 = (from model in models
from placement in model.Placements
select placement).ToList();
i guess i don't understand how it knows to concatenate the List<int> s
if i select placement, and then ToList, i should get a List<List<int>>
EDIT 2
ok, so actaully the cross join
from aModel in Models
from aPlacement in aModel.Placements
produces a tuple of { Model, Placements }
but these tuples are implicity concatenated with each other and all you have to do is to exclude the outer label, the Model
EDIT 3 i suppose it cannot be considered a cross join, although it feels like one, but since the placements are already segregated into separate lists, it is really just a full breadth and depth tree traversal,
Which could only be accurately described by a M-soft-ism, SelectMany
i am getting this idea from the type reported by ToString for the var. It has type of SelectMany iterator.

Categories

Resources