looping through 2 lists to get the results - linq

I have two lists:
myObject object1 = new myObject(id = 1, title = "object1"};
myObject object2 = new myObject(id = 2, title = "object2"};
myObject object3 = new myObject(id = 3, title = "object3"};
//List 1
List<myObject> myObjectList = new List<myObject>{object1, object2, object3};
//List 2
List<int> idList = new List<int>{2, 3,5};
Now I need to get output as follows:
If a id is present in both the lists, I need to print "A",
if a id is present in list1 only, then I need to print "B",
...and if the id is present in list2 only, I need to print "C"
Can I use linq to achieve this?

I would simply use the inbuilt functions of Except and Intersect
List1.Intersect(List2) = "A"
List1.Except(List2) = "B"
List2.Except(List1) = "C"
There are plenty of resources online about how you could go about doing this, as one example (I didn't look into it too much), check out this link - Linq - Except one list with items in another
Hope this does the trick...

Related

Linq Query Filter from two lists where row differs

Not sure how to formulate this Linq query.
I have two lists, each of which contains HashCheck objects:
class HashCheck
{
public string Id {get; set;}
public string Hash {get; set;}
}
So, given
List<HashCheck> list1;
List<HashCheck> list2;
I need a query that will result in a list having rows where the Ids of the rows matches, but the Hash does not.
So for example
List1 =
{1, 12345,
2, 34323,
3, 34083,
4, 09887}
List2 =
{1, 00001, << matching id, not matching hash
2, 34323,
3, 11112, << matching id, not matching hash
4, 09887
5, 98845}
ResultList =
{1, 00001,
3, 11112}
NOTE: in List2, there is an extra row, it would be a bonus if this were included in the ResultList. But I know how to do that in a separate query if necessary.
Thanks for any help.
try this code:
var list3 = (from i in list1
from j in list2
where i.Id == j.Id && i.Hash != j.Hash
select new HashCheck() { Id = j.Id, Hash = j.Hash
}).ToList<HashCheck>();
You can use join. something like below code:
var list3 = (from i in list1
join j in list2 on i.Id equals j.Id
where i.Hash != j.Hash
select new HashCheck() { Id = j.Id, Hash = j.Hash
}).ToList<HashCheck>();
It looks like you want your result to contain the HashCheck objects from list2, which would simply mean:
var ans = list2.Where(hc2 => !list1.Any(hc1 => hc1.Id == hc2.Id && hc1.Hash == hc2.Hash));
e.g. return all list2 elements without a list1 element that matches in both Id and Hash.
If list1 (and/or list2) is very large and performance is a consideration, you can convert list1 to a Dictionary and do lookups against that:
var list1map = list1.ToDictionary(hc1 => hc1.Id, hc1 => hc1.Hash);
var ans2 = list2.Where(hc2 => !list1map.TryGetValue(hc2.Id, out var hash1) || hash1 != hc2.Hash);
Another alternative would be to implement Equals/GetHashCode for your class and then you can use LINQ Except.
Add the following methods to your class:
public override bool Equals(object other) => (other is HashCheck hco) ? Id == hco.Id && Hash == hco.Hash : false;
public override int GetHashCode() => (Id, Hash).GetHashCode();
Now the computation is simple:
var ans3 = list2.Except(list1);
NOTE: Implementing Equals/GetHashCode in this way can be problematic if your HashCode objects are not treated as immutable. Some collection classes really won't like it if the hash code of an object already stored in them changes.
Also, it would be best practice to implement operator== and operator!= as well and possibly IEquatable.

Linq: Group on list inside lists

Let's say I have a list of lists.
For each item in this list , I have a list of custom objects.
These objects are as such:
public string Field1
public string Field2
public string Field3
What I'd like to achieve through Linq: filter out of my list of lists all the objects which have the same three fields, which are not the first element of their list and keep only the first one.
So let's say I have two lists listA and list B in my list.
listA has three objects object1, object2 and object3.
object1.Field1 = "a" object1.Field2 = "A" object1.Field3 = "1"
object2.Field1 = "a" object2.Field2 = "B" object2.Field3 = "2"
object3.Field1 = "a" object3.Field2 = "C" object3.Field3 = "3"
listB has three objects object4, object5 and object6.
object4.Field1 = "a" object4.Field2 = "A" object4.Field3 = "1"
object5.Field1 = "a" object5.Field2 = "B" object5.Field3 = "2"
object6.Field1 = "a" object6.Field2 = "D" object6.Field3 = "3"
In this example, object1 and object4 are the same, but because they are first in their respective list, they are not filtered out.
However, object2 and object5 having the same three fields values, only one of them will be kept so that at then end of my process, I'll have my two list like so:
listA has three objects object1, object2 and object3.
object1.Field1 = "a" object1.Field2 = "A" object1.Field3 = "1"
object2.Field1 = "a" object2.Field2 = "B" object2.Field3 = "2"
object3.Field1 = "a" object3.Field2 = "C" object3.Field3 = "3"
listB has now two objects object4 and object6.
object4.Field1 = "a" object4.Field2 = "A" object4.Field3 = "1"
object6.Field1 = "a" object6.Field2 = "D" object6.Field3 = "3"
I've been scratching my head for hours about this to no avail. I cannot do a foreach list of lists look into all the other lists as it would cause a performance problem (I can potentially have 1000000 lists of lists).
Anyone with an idea for this?
Why does it have to be LINQ? A simple iterator block solves the problem quite nicely.
The code below assumes you have overridden Equals and GetHashCode in your object to check for equality on the 3 fields. If that is not possible, use a custom equality comparer instead (to be passed in the HashSet constructor)
static IEnumerable<List<T>> GetFilteredList<T>(IEnumerable<List<T>> input)
{
var found = new HashSet<T>();
foreach (var list in input)
{
var returnList = new List<T>(list.Capacity);
foreach (var item in list)
{
// the first item is always added
// any other items are only added if they were
// never encountered before
if (list.Count == 0 || !found.Contains(item))
{
found.Add(item);
returnList.Add(item);
}
}
yield return returnList;
}
}
If you can stick with IEnumerable<IEnumerable<T>> as the return value, another approach that only sweeps the input just once could be something like this (without creating intermediary lists):
static IEnumerable<IEnumerable<T>> GetFilteredList<T>(IEnumerable<List<T>> input)
{
var encounteredElements = new HashSet<T>();
foreach (var list in input)
{
yield return Process(list, encounteredElements);
}
}
static IEnumerable<T> Process<T>(IEnumerable<T> input,
HashSet<T> encounteredElements)
{
bool first = true;
foreach (var item in input)
{
if (first) yield return item;
if (!encounteredElements.Contains(item))
{
yield return item;
}
encounteredElements.Add(item);
first = false;
}
}

LINQ Get Grouped ID by condition

Hi I have a List so:
A 1
A 2
A 3
A 4
B 1
B 2
C 1
I want to select the letter that contains AT LEAST these 3 numbers: 1,2,3
So in this case would be selected the letter A.
Can you help me to write this as LINQ expression?
Thanks a lot!
First, make a collection of the numbers you require.
var required = new[] { 1, 2, 3 };
Then, group your pairings by letter.
var groupedPairings = pairings.GroupBy(p => Letter, p => Number);
Then, discard those pairings that don't have your three required items. (The logic here is "take the collection of required items, remove anything in the group, and make sure there is nothing left".)
var groupsWithRequired = groupedPairings
.Where(g => !required.Except(g).Any());
Now, if you just want the letters, you can simply do
var lettersWithRequired = groupsWithRequired.Select(g => g.Key);
or if you want a dictionary mapping from the letter to its collection of numbers, you can do
var dictionary = groupsWithRequired.ToDictionary(g => g.Key, g => g.ToArray());
var numbersForA = dictionary["A"]; // = {1, 2, 3, 4}
You could try this, although I don't feel it's the best answer:
var items = new List<Item>{
new Item{Name="A", Value=1},
new Item{Name="A", Value=2},
new Item{Name="A", Value=3},
new Item{Name="A", Value=3},
new Item{Name="A", Value=4},
new Item{Name="B", Value=1},
new Item{Name="B", Value=2},
new Item{Name="C", Value=1},
};
var values = new List<int>{1,2,3};
var query = items.GroupBy (i => i.Name)
.Where (i => i.Select (x => x.Value)
.Intersect(values).Count() == values.Count)
.Select (i => i.Key);
Where
class Item{
public string Name{get;set;}
public int Value{get;set;}
}

Linq List contains specific values

I need to know if the List I am working with contains only some specific values.
var list = new List<string> { "First", "Second", "Third" };
If I want to know if the List contain at least one item with the value "First" I use the Any keyword:
var result = list.Any(l => l == "First");
But how I can write a Linq expression that will return true/false only if the List contains "First" and "Second" values?
I'm not entirely sure what you want, but if you want to ensure that "First" and "Second" are represented once, you can do:
var result = list.Where(l => l == "First" || l =="Second")
.Distinct()
.Count() == 2;
or:
var result = list.Contains("First") && list.Contains("Second");
If you've got a longer "whitelist", you could do:
var result = !whiteList.Except(list).Any();
On the other hand, if you want to ensure that all items in the list are from the white-list and that each item in the white-list is represented at least once, I would do:
var set = new HashSet(list);
set.SymmetricExceptWith(whiteList);
var result = !set.Any();
EDIT: Actually, Jon Skeet's SetEquals is a much better way of expressing the last bit.
Your question is unclear.
From the first sentence, I'd expect this to be what you're after:
var onlyValidValues = !list.Except(validValues).Any();
In other words: after you've stripped out the valid values, the list should be empty.
From the final sentence, I'd expect this:
var validSet = new HashSet<string>(requiredValues);
var allAndOnlyValidValues = validSet.SetEquals(candidateSequence);
Note that this will still be valid if your candidate sequence contains the same values multiple times.
If you could clarify exactly what your success criteria are, it would be easier to answer the question precisely.
You can use Intersect to find matches:
var list = new List<string> { "First", "Second", "Third" };
var comparelist = new List<string> { "First", "Second" };
var test = list.Intersect(comparelist).Distinct().Count() == comparelist.Count();

Join 2 lists by order instead of condition in LINQ

How can I join 2 lists of equal lengths (to produce a 3rd list of equal length) where I do not want to specify a condition but simply rely on the order of items in the 2 lists.
Eg how can I join:
{1,2,3,4} with {5,6,7,8}
to produce:
{{1,5}, {2,6}, {3,7}, {4,8}}
I have tried the following:
from i in new []{1,2,3,4}
from j in new []{5,6,7,8}
select new { i, j }
but this produces a cross join. When I use join, I always need to specify the "on".
You could use Select in the first list, use the item index and access the element on the second list:
var a = new [] {1,2,3,4};
var b = new [] {5,6,7,8};
var qry = a.Select((i, index) => new {i, j = b[index]});
If you are using .Net 4.0, you can use the Zip extension method and Tuples.
var a = new [] {1,2,3,4};
var b = new [] {5,6,7,8};
var result = a.Zip(b, (an, bn) => Tuple.Create(an, bn));
Alternatively, you can keep them as arrays:
var resultArr = a.Zip(b, (an, bn) => new []{an, bn});
There is a half way solution, if you want to use query syntax. Half way in the sense that you need to use the Select method on both lists in order to get the indexes that you will use in the where clause.
int[] list1 = {1,2,3,4};
int[] list2 = {5,6,7,8};
var result = from item1 in list1.Select((value, index) => new {value, index})
from item2 in list2.Select((value, index) => new {value, index})
where item1.index == item2.index
select new {Value1 = item1.value, Value2 = item2.value};
The benefit with this solution is that it wont fail if the lists have different lengths, as the solution using the indexer would do.

Resources