C# how to remove duplicate objects from a list - c#

I want to remove duplicate objects from a list. My code works, but I'm still afraid I'll make a mistake. Especially if the amount of data is larger, this solution doesn't make sense to me. I ask for your comments on my code.
// Print the list with duplicates
PrintList(listWithDuplicates);
// This code is not working
noDuplicates = listWithDuplicates.Distinct().ToList();
// This code is working but I am not sure if it is good practice
// especially if I have a large number of data
noDuplicates = listWithDuplicates
.GroupBy(x => x.input1)
.Select(x => x.First())
.GroupBy(x => x.input2)
.Select(x => x.First())
.GroupBy(x => x.output1)
.Select(x => x.First())
.GroupBy(x => x.output2)
.Select(x => x.First())
.ToList();
// Print the list without duplicates
PrintList(noDuplicates);
Console.ReadLine();
}
class Data
{
public string input1 { get; set; }
public string input2 { get; set; }
public string output1 { get; set; }
public string output2 { get; set; }
}

You tried to use .Distinct() without further telling it how to compare instances of your Data-class.
Therefore you could create a Comparer class which you'd then pass to .Distinct() as a parameter:
public class Data
{
public string input1 { get; set; }
public string input2 { get; set; }
public string output1 { get; set; }
public string output2 { get; set; }
}
public class DataComparer : EqualityComparer<Data>
{
public override bool Equals(Data x, Data y)
{
if (x.input1 == y.input1 &&
x.input2 == y.input2 &&
x.output1 == y.output1 &&
x.output2 == y.output2)
{
return true;
}
return false;
}
public override int GetHashCode(Data obj)
{
return $"{obj.input1}{obj.input2}{obj.output1}{obj.output2}".GetHashCode();
}
}
Here is an example:
var dataList = new List<Data>()
{
new Data(){ input1="A", input2="B", output1="B", output2="A"},
new Data(){ input1="A", input2="B", output1="B", output2="A"},
new Data(){ input1="C", input2="D", output1="D", output2="C"},
new Data(){ input1="C", input2="D", output1="D", output2="C"}
};
dataList = dataList.Distinct(new DataComparer()).ToList();

A friend showed me how to do the job without a comparer and without override methods.
noDuplicates = listWithDuplicates.GroupBy(x => new { x.input1, x.input2, x.output1, x.output2 }).Select(y => y.First()).ToList();

Related

Linq issues for a complex request

I have a list of users and each users have list of hashtags.
In my app I provide a way on searching results by hashtags.
In my api I split the search string to get each hashtags separately and what I try to have is:
The user entity, the number of hashtags in common and the hashtags related.
For example:
My classes are as below:
public class UserDto
{
public List<HashtagDto> Hashtags { get; set; }
}
public class HashtagDto
{
string name { get; set; }
}
public class UserHashtagSearchResultDto
{
public UserDto UserFk { get; set; }
public int CountResults { get; set; }
public List<HashtagDto> HashtagsFk { get; set; }
}
My query:
if (searchText.IsNullOrEmpty())
return null;
var splits = searchText.Split(new string[] { " " }, System.StringSplitOptions.RemoveEmptyEntries);
List<string> hashes = HashtagHelper.ToHashkeys(splits);
if (hashes != null && hashes.Count() > 0)
{
User.GetUsers().Include(h => h.Hashtags).Where(s => hashes.Contains(....).Select((hs) =>
{
return new UserHashtagSearchResultDto
{
}
});
}
Input will be: running swimming
Output will be:
List
Example:
Result1
{
UserFk=User1
CountResults=2 (the two hashtags of this user exists in the hashtag repository)
HashtagsFk=List<Hashtag>{hashtag1, hashtag2}; // (swimming and running}
}
Result1
{
UserFk=User2
CountResults=1 (only the hashtag swimming existsin the hashtag repository)
HashtagsFk=List<Hashtag>{hashtag1}; // (swimming)
}
I don't see how to do it by linq.
Ok this is an answer to my issue.
var userCommon = _userRepository.GetAll().Include(h => h.HashtagUsers).ThenInclude(h => h.HashtagFk)
.Where(s => hashes.Intersect(s.HashtagUsers.Select(h => h.HashtagFk.Code).AsEnumerable()).Count() > 0);
userCommon.ForEach(res =>
{
var result = new HashtagResultSearchDto();
result.UserFk = ObjectMapper.Map<UserDto>(res);
var d = res.HashtagUsers.AsQueryable().Select(h => h.HashtagFk);
result.CountResult = result.Hashtags.Count();
result.UserId = res.Id;
hashtagResultSearchDtos.Add(result);
});

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.

Applying a Linq to Entities join on my code

I have code that works, but I worked around a 'Join' in Linq to Entities, because I could not figure it out.
Could you please show me how to succesfully apply it to my code?
My desired result is a dictionary:
Dictionary<string, SelectedCorffData> dataSelectedForDeletion = new Dictionary<string, SelectedCorffData>();
The above mentioned class:
public class SelectedCorffData
{
public long CorffId { get; set; }
public string ReportNumber { get; set; }
public DateTime CorffSubmittedDateTime { get; set; }
}
Please note the 'intersectResult' I am looping through is just a string collection.
Here is my code:
DateTime dateToCompare = DateTime.Now.Date;
Dictionary<string, SelectedCorffData> dataSelectedForDeletion = new Dictionary<string, SelectedCorffData>();
foreach (var mafId in intersectResult)
{
var corffIdsPerMaf = context
.Mafs
.Where(m => m.MafId == mafId)
.Select(m => m.CorffId);
var corffIdForMaf = context
.Corffs
.Where(c => corffIdsPerMaf.Contains(c.Id))
.OrderByDescending(c => c.CorffSubmittedDateTime)
.Select(c => c.Id)
.First();
//Selected close-out forms, whose MAF's may be up for deletion, based on date.
var corffData = context
.Corffs
.Where(c => c.Id == corffIdForMaf && System.Data.Entity.DbFunctions.AddYears(c.CorffSubmittedDateTime, 1).Value > dateToCompare)
.Select(c => new SelectedCorffData () { CorffId = c.Id, ReportNumber = c.ReportNumber, CorffSubmittedDateTime = c.CorffSubmittedDateTime })
.FirstOrDefault();
if(corffData != null)
{
dataSelectedForDeletion.Add(mafId, corffData);
}
}
Please note: this is not just a simple join. If it can't be simplified, please tell me. Also please explain why.
The code below I don't think is exactly right but it is close to what you need. I simulated the database so I could get the syntax correct.
namespace System
{
namespace Data
{
namespace Entity
{
public class DbFunctions
{
public static Data AddYears(DateTime submittedTime, int i)
{
return new Data();
}
public class Data
{
public int Value { get; set; }
}
}
}
}
}
namespace ConsoleApplication23
{
class Program
{
static void Main(string[] args)
{
Context context = new Context();
int dateToCompare = DateTime.Now.Year;
var corffIdsPerMaf = context.Mafs.Select(m => new { id = m.CorffId, mafs = m}).ToList();
var corffIdForMaf = context.Corffs
.Where(c => System.Data.Entity.DbFunctions.AddYears(c.CorffSubmittedDateTime, 1).Value > dateToCompare)
.OrderByDescending(c => c.CorffSubmittedDateTime).Select(c => new { id = c.Id, corff = c}).ToList();
var intersectResult = from p in corffIdsPerMaf
join f in corffIdForMaf on p.id equals f.id
select new SelectedCorffData() { CorffId = p.id, ReportNumber = f.corff.ReportNumber, CorffSubmittedDateTime = f.corff.CorffSubmittedDateTime };
Dictionary<string, SelectedCorffData> dataSelectedForDeletion = intersectResult.GroupBy(x => x.ReportNumber, y => y).ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
public class Context
{
public List<cMafs> Mafs { get; set;}
public List<cCorffs> Corffs { get; set;}
}
public class cMafs
{
public int CorffId { get; set; }
}
public class cCorffs
{
public DateTime CorffSubmittedDateTime { get; set; }
public int Id { get; set; }
public string ReportNumber { get; set; }
}
public class Test
{
}
public class SelectedCorffData
{
public long CorffId { get; set; }
public string ReportNumber { get; set; }
public DateTime CorffSubmittedDateTime { get; set; }
}
}

How to add iOrderedQueryable results to a list?

I'm trying to add my database results to list. Here's my class:
class MyClass
{
public string Tick { get; set; }
public string Exchange { get; set; }
public double Price { get; set; }
public double Volume { get; set; }
}
Here's my routine:
var myList = new List<MyClass>();
foreach (var element in compsList)
{
myList.Add(Price_tbl
.Where(p => p.Ticker.Equals(element.Tick) && p.Tick_datetime <= DateTime.Today)
.GroupBy(p => new { Comp = p.Ticker })
.Select(p => new MyClass {
Tick = p.Key.Comp,
Exchange = element.Exchange,
Price = p.OrderByDescending(c => c.Tick_datetime).Select(c => c.Close_price).FirstOrDefault(),
Volume = (p.Sum(c => c.Volume) / 52),
}));
}
But I'm getting the error:
cannot convert from 'System.Linq.IQueryable<UserQuery.MyClass>' to 'UserQuery.MyClass'
Is there another way to add the results to a list? (I've been racking my brain on this for a few hours now.)

Object Type conversion while doing Group By

I have a List. I need to find the unique ExistingData records by applying Group By. Following code works.
var distinctItemsWorking = myCostPages
.GroupBy(x => new {
x.CostPageContent.Program,
x.CostPageContent.Group,
x.CostPageContent.Sequence })
.Select(y => y.First());
Now I need to convert the unique list into a List. How can we achieve this conversion when we do Grouping?
C# Method
public List<CostPage> GetCostPages(SearchEntity search, int pageIndex, int pageSize)
{
List<ExistingData> AllData = GetExistingData();
var allMatchingValues = from existingDatas in AllData
where existingDatas.CostPageContent.Program == search.Program
select existingDatas;
var query = allMatchingValues;
List<ExistingData> currentSelectionForExistingData = query
.Skip(pageIndex * pageSize)
.Take(pageSize)
.ToList();
//var distinctItems = currentSelectionForExistingData.GroupBy(x => new { x.CostPageContent.Program, x.CostPageContent.Group, x.CostPageContent.Sequence })
// .Select(y => new CostPage()
// {
// CostPageContent = y.CostPageContent
// }
// );
var distinctItemsWorking = currentSelectionForExistingData.GroupBy(x => new { x.CostPageContent.Program, x.CostPageContent.Group, x.CostPageContent.Sequence })
.Select(y => y.First());
List<CostPage> myCostPages = new List<CostPage>();
foreach (ExistingData exist in distinctItemsWorking)
{
CostPage c = new CostPage();
c.CostPageContent = exist.CostPageContent;
myCostPages.Add(c);
}
return myCostPages;
}
Other Classes
public class ExistingData
{
public CostPageNumberContent CostPageContent { get; set; }
public string ItemID { get; set; }
}
public class CostPage
{
public CostPageNumberContent CostPageContent { get; set; }
}
public class CostPageNumberContent
{
public string Program { get; set; }
public string Group { get; set; }
public string Sequence { get; set; }
}
public class SearchEntity
{
public string Program { get; set; }
public string Sequence { get; set; }
public string ItemID { get; set; }
}
If you are trying to replace the foreach, you can do something like this:
var myCostPages = currentSelectionForExistingData
.GroupBy(x => new { x.CostPageContent.Program, x.CostPageContent.Group,
x.CostPageContent.Sequence })
.Select(y => new CostPage { CostPageContent = y.First().CostPageContent })
.ToList();
Putting the creation of the CostPage objects into GroupBy would make no sense. The Select is the correct place to perform this conversion.

Categories