Linq between two list with condition - c#

I have settings class
public class Setting
{
public virtual string Key { get; set; }
public virtual string Value { get; set; }
}
and i have to list
IEnumerable<Setting> A1 => contain {"A","1"}{"B","2"}
IEnumerable<Setting> A2 => contain {"A","1"}{"B","5"}
i want linq statment to chose the element from list A2 that have same key and different value here is {"B","5"}
I have try
A2.Where(x => A1.Any(y => y.Value != x.Value)).ToList();
this give me the two elemnts in A2
can any one help me
thank you
**Edit **
my settings class
public class Setting : Entity<int>
{
public virtual DateTime? ModificationDate { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual string Key { get; set; }
public virtual string Value { get; set; }
public virtual string ModifiedBy { get; set; }
public virtual string Type { get; set; }
public virtual string ValidateRegex { get; set; }
public virtual bool IsSystem { get; set; }
}
and i have return from mvc IEnumerable<Setting> let it name settings,
then i get from database the original settings IEnumerable<Setting> let it name dbsettings
i want to know the changed value from settings to make update on it

You need to compare the Key as well:
A2.Where(x => A1.Any(y => y.Key == x.Key && y.Value != x.Value)).ToList();
The following sample returns { "B", "5" } as the result:
void Main()
{
var a1 = new List<Setting>(new Setting[] {
new Setting() { Key = "A", Value = "1" },
new Setting() { Key = "B", Value = "2" } });
var a2 = new List<Setting>(new Setting[] {
new Setting() { Key = "A", Value = "1" },
new Setting() { Key = "B", Value = "5" } });
var result = a2.Where(x => a1.Any(y => y.Key == x.Key && y.Value != x.Value)).ToList();
Console.WriteLine(result);
}
As you are comparing strings, you should be aware that == and != respectively always compares case-sensitive. So the keys need to be written in the same way in both lists (and also differences in case will be recognized as relevant differences). You can also use an overload of string.Compare to specify the comparison options in greater detail.

This should do it:
A2.Where(x => A1.Any(y => y.Key == x.Key && y.Value != x.Value))
BTW, your Setting class seems like reinventing the wheel. Dictionary, Tuple and NameValueCollection can all do that for you.

var A1 = new List<Setting>(){new Setting(){Key = "A", Value = "1"}};
var A2 = new List<Setting>() { new Setting() { Key = "A", Value = "2" } };
var a1Keys = A1.Select(x => x.Key).ToList();
var dupKeys = A2.Where(x => a1Keys.Contains(x.Key)).Select(x=>x.Key);
var res = A2.Where(x => dupKeys.Contains(x.Key));

For performance reasons you could use a lookup:
var a1KeyLookup = A1.ToLookup(x => x.Key);
List<Setting> a2List = A2
.Where(a2 => a1KeyLookup[a2.Key].Any(a1 => a1.Value != a2.Value))
.ToList();
Here's your sample data:
IEnumerable<Setting> A1 = new List<Setting> {
new Setting{Key="A", Value="1"},
new Setting{Key="B", Value="2"},
};
IEnumerable<Setting> A2 = new List<Setting> {
new Setting{Key="A", Value="1"},
new Setting{Key="B", Value="5"},
};
var a1KeyLookup = A1.ToLookup(x => x.Key);
List<Setting> a2List = A2
.Where(a2 => a1KeyLookup[a2.Key].Any(a1 => a1.Value != a2.Value))
.ToList();
It returns as expected a list with a single item: Key="B", Value="5"

Related

Converting list from one class to another

I'm trying to convert a group a complex list in C# (with Linq)
public class classA
{
public string Name { get; set; }
public int id { get; set; }
public string phone { get; set; }
public string interest { get; set; }
}
My first class is classA where it contains many list of elements like below.
List<classA> obj = new List<classA>();
obj.Add(new classA { id = 1, Name = "a", phone = "321", interest = "Playing" });
obj.Add(new classA { id = 1, Name = "2", phone = "123", interest="Tv" });
From this I need to group by using the id, So I've used Linq
var item = obj.GroupBy(a => a.id).Select(ac => ac.ToList()).ToList();
I've another class called classB which hold's the values others than id from the classA (where it'd be hold all subset of different attributes)
public class classB
{
public string Name { get; set; }
public string phone { get; set; }
public string interest { get; set; }
}
My Final Class looks likes,
public class Final
{
public int id { get; set; }
public List<classB> details { get; set; }
public Final()
{
details = new List<classB>();
}
}
My requirements are, after grouping the classA based on id, I need to convert that into my final class.
So I did like below,
public static void Main()
{
Console.WriteLine("Hello World");
List<classA> obj = new List<classA>();
obj.Add(new classA { id = 1, Name = "a", phone = "321", interest = "Playing" });
obj.Add(new classA { id = 1, Name = "b", phone = "123", interest = "Tv" });
obj.Add(new classA { id = 2, Name = "c", phone = "12322", interest = "Tv" });
obj.Add(new classA { id = 3, Name = "d", phone = "12333", interest = "Tv" });
var item = obj.GroupBy(a => a.id).Select(ac => ac.ToList()).ToList();
List<Final> finalobjList = new List<Final>();
foreach (var report in item)
{
Final finalObj = new Final();
foreach (var result in report)
{
finalObj.id = result.id;
}
var data = report.Select(x => new classB { Name = x.Name, phone = x.phone, interest = x.interest }).ToList();
finalObj.details = data;
finalobjList.Add(finalObj);
}
Console.WriteLine(finalobjList.Count());
}
I believe there is another easy way to achieve this using linq without using foreach multiple times
Appreciate your help!
You should be able to use your existing code except when you do your Select, select a new Final and use the group's Key for the Id, and convert the ac.ToList to a list of ClassB for the Details:
var item = obj
.GroupBy(a => a.id)
.Select(ac =>
new Final
{
Id = ac.Key,
Details = ac.Select(a =>
new classB {interest = a.interest, phone = a.phone, Name = a.Name})
.ToList()
});
var finalobjList = obj.GroupBy(a => a.id).Select(x => new Final() { id = x.Key, details = x.Select(y => new classB() { Name = y.Name }).ToList() } ).ToList();
(Code only answer - please dont hate me)
var items = (from a in obj
group new classB {Name = a.Name, phone = a.phone, interest = a.interest} by a.id into aa
select new Final { id = aa.Key, B= aa.ToList()}).ToList();

Best approach to compare if one list is subset of another in C#

I have the below two classes:
public class FirstInner
{
public int Id { get; set; }
public string Type { get; set; }
public string RoleId { get; set; }
}
public class SecondInner
{
public int Id { get; set; }
public string Type { get; set; }
}
Again, there are lists of those types inside the below two classes:
public class FirstOuter
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public List<FirstInner> Inners { get; set; }
}
public class SecondOuter
{
public int Id { get; set; }
public string Name { get; set; }
public List<SecondInner> Inners { get; set; }
}
Now, I have list of FirstOuter and SecondOuter. I need to check if FirstOuter list is a subset of SecondOuter list.
Please note:
The names of the classes cannot be changed as they are from different systems.
Some additional properties are present in FirstOuter but not in SecondOuter. When comparing subset, we can ignore their presence in SecondOuter.
No.2 is true for FirstInner and SecondInner as well.
List items can be in any order---FirstOuterList[1] could be found in SecondOuterList[3], based on Id, but inside that again need to compare that FirstOuterList[1].FirstInner[3], could be found in SecondOuterList[3].SecondInner[2], based on Id.
I tried Intersect, but that is failing as the property names are mismatching. Another solution I have is doing the crude for each iteration, which I want to avoid.
Should I convert the SecondOuter list to FirstOuter list, ignoring the additional properties?
Basically, here is a test data:
var firstInnerList = new List<FirstInner>();
firstInnerList.Add(new FirstInner
{
Id = 1,
Type = "xx",
RoleId = "5"
});
var secondInnerList = new List<SecondInner>();
secondInner.Add(new SecondInner
{
Id = 1,
Type = "xx"
});
var firstOuter = new FirstOuter
{
Id = 1,
Name = "John",
Title = "Cena",
Inners = firstInnerList
}
var secondOuter = new SecondOuter
{
Id = 1,
Name = "John",
Inners = secondInnerList,
}
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
Need to check if firstOuterList is part of secondOuterList (ignoring the additional properties).
So the foreach way that I have is:
foreach (var item in firstOuterList)
{
var secondItem = secondOuterList.Find(so => so.Id == item.Id);
//if secondItem is null->throw exception
if (item.Name == secondItem.Name)
{
foreach (var firstInnerItem in item.Inners)
{
var secondInnerItem = secondItem.Inners.Find(sI => sI.Id == firstInnerItem.Id);
//if secondInnerItem is null,throw exception
if (firstInnerItem.Type != secondInnerItem.Type)
{
//throw exception
}
}
}
else
{
//throw exception
}
}
//move with normal flow
Please let me know if there is any better approach.
First, do the join of firstOuterList and secondOuterList
bool isSubset = false;
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
var jointOuterList = firstOuterList.Join(
secondOuterList,
p => new { p.Id, p.Name },
m => new { m.Id, m.Name },
(p, m) => new { FOuterList = p, SOuterList = m }
);
if(jointOuterList.Count != firstOuterList.Count)
{
isSubset = false;
return;
}
foreach(var item in jointOuterList)
{
var jointInnerList = item.firstInnerList.Join(
item.firstInnerList,
p => new { p.Id, p.Type },
m => new { m.Id, m.type },
(p, m) => p.Id
);
if(jointInnerList.Count != item.firstInnerList.Count)
{
isSubset = false;
return;
}
}
Note: I am assuming Id is unique in its outer lists. It means there will not be multiple entries with same id in a list. If no, then we need to use group by in above query
I think to break the question down..
We have two sets of Ids, the Inners and the Outers.
We have two instances of those sets, the Firsts and the Seconds.
We want Second's inner Ids to be a subset of First's inner Ids.
We want Second's outer Ids to be a subset of First's outer Ids.
If that's the case, these are a couple of working test cases:
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsTrue(isInnerSubset);
Assert.IsTrue(isOuterSubset);
}
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreNotSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
firstInnerIds.Clear();
firstInnerIds.Add(5);
firstOuterIds.Clear();
firstOuterIds.Add(5);
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsFalse(isInnerSubset);
Assert.IsFalse(isOuterSubset);
}
private List<FirstOuter> GetFirstOuterList() { ... }
private List<SecondOuter> GetSecondOuterList() { ... }

compare 2 properties inside 2 linq lists and return a boolean based on the comparision

I've tried to find the answer to this but nothing seems to fit it quite right.
Requirement: From a known list of FooObjects, return a list of the Foo Id's whose data satisfies all the search criteria.
Here is my code:
class testClass
{
public class SearchItem
{
string Term { get; set; }
decimal Confidence { get; set; }
}
public class FooObject
{
public Guid Id { get; set; }
public List<Data> Data { get; set; }
}
public class Data
{
public string Text { get; set; }
public decimal Confidence { get; set; }
}
[Test]
public void Test()
{
var searchItems = new List<SearchTerm>
{
new SearchTerm{ Confidence = (decimal)1, Term = "TestWord" },
new SearchTerm{ Confidence = (decimal)1, Term = "TestWord2" },
};
var FooObjects = new List<FooObject>
{
new FooObject{Id = new Guid(), Data = new List<Data>
{
new Data{Text = "TestWord", Confidence = 1},
new Data{Text = "TestWord2", Confidence = 1},
new Data{Text = "SomeOtherWord", Confidence = 1},
}
}
};
//result is a list of the Foo IDs
var result = FooObjects.Where(foo => !searchItems.Select(item => item.Term).Except(foo.Data.Select(dt => dt.Text).Distinct()).Any())
.Select(foo => foo.Id).ToList();
Assert.That(result.Count, Is.EqualTo(1));
searchItems.Add(new SearchTerm{Term = "NotFoundString"});
result = FooObjects.Where(foo => !searchItems.Select(item => item.Term).Except(foo.Data.Select(dt => dt.Text).Distinct()).Any())
.Select(foo => foo.Id).ToList();
Assert.That(result.Count, Is.EqualTo(0));
}
}
I now need to modify this so that I can compare against the confidence of each word
Question:
How do I modify the LINQ to compare the confidence and the Term against my data
Instead of matching any criteria like #dymanoid said in his answer, you should be looking to satisfy all the search items/terms (you are mixing these up in your example code, be consistent).
var result = FooObjects
.Where(f => searchItems.All(
s => f.Data.Exists(d => d.Text == s.Term && d.Confidence == s.Confidence)))
.Select(f => f.Id);
Maybe you're looking for something like this:
var result = FooObjects
.Where(foo => foo.Data.Any(d => searchTerms.Any(
si => d.Term == si.Text && d.Confidence == si.Confidence)))
.Select(foo => foo.Id);
Keep in mind that this search isn't effective - if your data sets are large, the performance will be bad.

How could distinct multi array list on c#

Consider objModels is object of ChildModel class
public class ChildModel
{
public string Title { get; set; }
public long Id { get; set; }
}
It has some data like:
title,id: a,1 b,1 a,1
Two of these data are same, and after distinct it could be like: a,1 b,1
Question is how could I distinct it on c#
List<ChildModel> obj= objModels.ToList();
Also these aren't help
objModels.Distinct();
obj.Distinct();
You could use a library named MoreLINQ
This is the query you could use with MoreLINQ to find elements that are distinct by multiple properties:
var query = objModels.DistinctBy(p => new { p.Id, p.Title});
try this:
var answer= obj.DistinctBy(p => new { p.Id , p.Title });
and check this link for other way
I would do the following:
namespace ConsoleApplication7
{
class Program
{
static void Main()
{
List<ChildModel> list = new List<ChildModel>();
list.Add(new ChildModel { Title = "a", Id = 1 });
list.Add(new ChildModel { Title = "b", Id = 1 });
list.Add(new ChildModel { Title = "a", Id = 1 });
var x = list.Distinct(new ChildModelComparer()).ToList();
var y = x; //This has only got two child items.
}
}
class ChildModelComparer : IEqualityComparer<ChildModel>
{
public bool Equals(ChildModel x, ChildModel y)
{
return x.Id.Equals(y.Id) && x.Title.Equals(y.Title);
}
public int GetHashCode(ChildModel obj)
{
if (string.IsNullOrEmpty(obj.Title) && obj.Id == 0)
{
return 0;
}
return $"{obj.Id}{obj.Title}".GetHashCode();
}
}
public class ChildModel
{
public string Title { get; set; }
public long Id { get; set; }
}
}
you can use .GroupBy:
var result= obj
.GroupBy(p => p.Title)
.Select(g => g.First()) // choose which one
.ToList();
edit: if you want to GroupBy more than one Property you can just change p.Title to new {p.Title, p.Id} like so
var result= obj
.GroupBy(p => new {p.Title, p.Id})
.Select(g => g.First()) // choose which one
.ToList();

How to have this result with Linq Request?

This is my list
int specifiedvalue=6.5;
Name Value
A 6.5
A 6.0
B 6.5
B 6.0
C 7.75
D 7.0
I would like to remove from this list objects which have the same name and a different value than the specifiedvalue(6.5) and keep the rest.
The result should be like:
A 6.5
B 6.5
C 7.75
D 7.0
Thanks
First of all you wrote that, the type of the specifiedValue is int. But the vaue is floating point and you must change it as double.
I am supposing that, you have declared your class like that:
public class Lens
{
public string Name { get; set; }
public double Value { get; set; }
public Lens(string name, double value)
{
Name = name;
Value = value;
}
}
Here I am initializing the objects:
double specidifedValue = 6.5;
List<Lens> pairs = new List<Lens>();
pairs.Add(new Lens("A", 6.5));
pairs.Add(new Lens("A", 6.0));
pairs.Add(new Lens("B", 6.5));
pairs.Add(new Lens("B", 6.0));
pairs.Add(new Lens("C", 7.75));
pairs.Add(new Lens("D", 7.0));
And with that, firstly I am finding Names which are occured more than one time in the list. And then selecting those with the value 6.5.
var keysMoreThanOne = pairs.GroupBy(x => x.Name)
.Where(x => x.Count() > 1).Select(x => x.Key).ToList();
List<Lens> filteredPairs = pairs
.Where(x => (keysMoreThanOne.Contains(x.Name) && x.Value == specidifedValue)
|| !keysMoreThanOne.Contains(x.Name)).ToList();
The result is as you want.
Update:
var result = new List<Lens>();
var keysMoreThanOne = pairs.GroupBy(x => x.Name).Where(x => x.Count() > 1).Select(x => x.Key).ToList();
if (specidifedValue > 0)
{
result = pairs.Where(x => (keysMoreThanOne.Contains(x.Name) && x.Value == specidifedValue) ||
!keysMoreThanOne.Contains(x.Name)).ToList();
}
else
{
result = pairs.Where(x => (keysMoreThanOne.Contains(x.Name) &&
x.Value == pairs.Where(y=> y.Name==x.Name).OrderByDescending(y=> y.Value).First().Value)
|| !keysMoreThanOne.Contains(x.Name)).ToList();
}
internal class NameValue
{
public string Name { get; set; }
public double Value { get; set; }
}
var sourceList = new List<NameValue>
{
new NameValue {Name = "A", Value = 6.5},
new NameValue {Name = "A", Value = 6.0},
new NameValue {Name = "B", Value = 6.5},
new NameValue {Name = "B", Value = 6.0},
new NameValue {Name = "C", Value = 7.75},
new NameValue {Name = "D", Value = 7.0}
};
var result = sourceList.GroupBy(x => x.Name)
.Select(x => new
{
Name = x.Key,
Value = x.Max(y => y.Value)
});

Categories