remove a duplicate list in c# - c#

How do I remove a duplicate listing?
My class :
public class test
{
public string UserName { get; set; }
public List<int> Scores { get; set; }
}
Sample data
var tests = new List<test> {
new test
{
Scores = new List<int> { 1, 2, 3 },
UserName = "user1"
},
new test
{
Scores = new List<int> { 1, 5, 3 },
UserName = "user2"
},
new test
{
Scores = new List<int> { 1, 2, 3 },
UserName = "user3"
}
};
I need to remove duplicates based on the score.
For example, the above list is repeated scores of the two users.I need to remove one of the duplicated users. No matter what the user is removed.Only one is removed. I have no idea to do it.
Scenario : user1 and user3 is Repeated.
user1 or user3 Should be removed.One of the two should to stay

Following query groups tests by first test instance, which have same scores. Then you just select group keys:
from t in tests
group t by tests.First(x => Enumerable.SequenceEqual(x.Scores, t.Scores)) into g
select g.Key;
Returns user1 and user2. Same with lambda syntax:
tests.GroupBy(t => tests.First(x => Enumerable.SequenceEqual(x.Scores,t.Scores)))
.Select(g => g.Key);
If you want last user to stay, then change First to Last, but First is more efficient here, because it stops enumeration earlier.

You could use Linq function Distinct() (tests.Distinct(...))
and provide a IEqualityComparer instance that checks if your scores are identical

There are 2 ways you can do this:
Make a for loop and remove double entries:
var namesFound = new List<string>();
for (int i = 0; i < tests.Count; i++)
{
if (!namesFound.Contains(tests[i].UserName))
{
namesFound.Add(tests[i].UserName);
}
else
{
tests.Remove(tests[i]);
i--;
}
}
Implement IEqualityComparer on your class test. Check this link: http://msdn.microsoft.com/en-us/library/ms132151(v=vs.110).aspx
After that check for equality by using tests[0] == tests[1] and remove the item when they are equal

Provide a custom IEqualityComparer<test> to Distinct method and get the job done.
internal class TestComparer : IEqualityComparer<test>
{
public bool Equals(test x, test y)
{
if (x.Scores.Count != y.Scores.Count)
return false;
return new HashSet<int>(x.Scores).SetEquals(y.Scores);
}
public int GetHashCode(test obj)
{
return obj.Scores.Aggregate((seed,x)=> seed | x);
}
}
var filteredList = tests.Distinct(new TestComparer()).ToList();

Related

Get a unique list (by key) from a list-in-list

I have a list of requests. Each request has many approvers. I want to go through all the requests and their approvers and get a list of unique approvers and their requests.
Here are sample models:
var requestsToProcess = await GetBatchOfApprovedRequestsAsyn(); // new List<RequestModel>();
public class RequestModel
{
public RequestModel()
{
ApproversList = new List<RequestApproverModel>();
}
public long Id { get; set; } // Key
public string Brief { get; set; }
public string Description { get; set; }
public List<RequestApproverModel> ApproversList { get; set; }
}
public class RequestApproverModel
{
public string Email { get; set; } // Key
public string FullName { get; set; }
}
I know how to get unique tuple from a list but don't understand if the target list is on an element of another list.
Basically the premise, is flatten and project, then groupby, then optionally project again.
Given
var requests= new List<RequestModel>()
{
new()
{
Id = 1,
ApproversList = new List<RequestApproverModel>()
{
new(){Email = "bob"},
new(){Email = "dole"}
}
},
new()
{
Id = 2,
ApproversList = new List<RequestApproverModel>()
{
new(){Email = "bob"},
new(){Email = "blerg"}
}
}
};
Example
var results =requests.SelectMany(request =>
request.ApproversList,
(request, approver) => new {request, approver})
.GroupBy(x => x.approver.Email )
.Select(x => new { Approver = x.Key, Requests = x.Select(y => y.request).ToList() });
foreach (var item in results)
{
Console.WriteLine(item.Approver);
foreach (var request in item.Requests)
Console.WriteLine(" " + request.Id);
}
Output
bob
1
2
dole
1
blerg
2
The two complementary methods you need from LINQ are SelectMany, which unpacks a list-of-lists to a list, and GroupBy, which packs a list to a list-of-lists (you need to go from a-of-b to b-of-a)
var result = someRequestModels
.SelectMany(rm => rm.ApproversList, (rm, ram) => new { RM = rm, RamEmail = ram.Email })
.GroupBy(at => at.RamEmail, at => at.RM);
The SelectMany is like a nested pair of foreach
foreach(var rm in someRequestModels)
foreach(var ram in rm.ApproversList)
flatlist.Add(new { rm, ram});
This has turned the list of lists into a single list, repeating the RequestModel over and over per RequestApproverModel. You can then run a GroupBy of approver Email which takes every unique email in the flattened list and puts together a list of list of RequestModels. In non LINQ pseudocode it'd look something like:
foreach(var rmRamPair In flatlist)
grouping[rmRamPair.Email].Add(rmRamPair.Rm);
This produces an IGrouping which is something like a list of lists, where each entry has a Key, a string of the approver's email and is an enumerable of all the requestmodels they have, so eg
foreach(var x in result){
Console.WriteLine($"approver with email of {x.Key} has cases:";
foreach(var rm in x)
Console.WriteLine($"id is '{rm.Id}' and Brief is '{rm.Brief}'");
}
If it makes you more comfortable, you can call ToDictionary(x => x.Key, x => x.ToList()) on the result and you'll get a Dictionary<string, List<RequestModel>> out, the email being the key and and list of requestmodels being the value
If you want the whole RequestApproverModel, not just the email it might be a bit more tricky. It's easy if you've reused instances of RAM so if there is literally only one object in memory that is "bob#mail.com" and that object is present on a couple of different requests:
var ram = new RequestApproverModel{ Email = "bob#mail.com" };
var r1 = new RequestModel();
r1.ApproversList.Add(ram);
var r2 = new RequestModel();
r2.ApproversList.Add(ram);
Here the instance is the same one; you can just group by it instead of the email.
If you've ended up with objects that look the same but are at different memory addresses:
var r1 = new RequestModel();
r1.ApproversList.Add(new RequestApproverModel{ Email = "bob#mail.com" });
var r2 = new RequestModel();
r2.ApproversList.Add(new RequestApproverModel{ Email = "bob#mail.com" });
Then the standard implementation of Equals and GetHashcode(inherited from object) is useless because it's based on the memory stress where the instances live.
Your RequestModel class will instead need to implement Equals and GetHashcode that report equality based on Email, otherwise grouping by the whole RequestModel won't work out

Filtering nested lists with nullable property

Say I have the following class structures
public class EmailActivity {
public IEnumerable<MemberActivity> Activity { get; set; }
public string EmailAddress { get; set; }
}
public class MemberActivity {
public EmailAction? Action { get; set; }
public string Type { get; set; }
}
public enum EmailAction {
None = 0,
Open = 1,
Click = 2,
Bounce = 3
}
I wish to filter a list of EmailActivity objects based on the presence of a MemberActivity with a non-null EmailAction matching a provided list of EmailAction matches. I want to return just the EmailAddress property as a List<string>.
This is as far as I've got
List<EmailAction> activityTypes; // [ EmailAction.Open, EmailAction.Bounce ]
List<string> activityEmailAddresses =
emailActivity.Where(
member => member.Activity.Where(
activity => activityTypes.Contains(activity.Action)
)
)
.Select(member => member.EmailAddress)
.ToList();
However I get an error message "CS1503 Argument 1: cannot convert from 'EmailAction?' to 'EmailAction'"
If then modify activityTypes to allow null values List<EmailAction?> I get the following "CS1662 Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type".
The issue is the nested .Where it's returning a list, but the parent .Where requires a bool result. How would I tackle this problem?
I realise I could do with with nested loops however I'm trying to brush up my C# skills!
Using List.Contains is not ideal in terms of performance, HashSet is a better option, also if you want to select the email address as soon as it contains one of the searched actions, you can use Any:
var activityTypes = new HashSet<EmailAction>() { EmailAction.Open, EmailAction.Bounce };
List<string> activityEmailAddresses =
emailActivity.Where(
member => member.Activity.Any(
activity => activity.Action.HasValue &&
activityTypes.Contains(activity.Action.Value)
)
)
.Select(activity => activity.EmailAddress)
.ToList();
You want to use All or Any depends if you want each or at least one match...
HashSet<EmailAction> activityTypes = new HashSet<EmailAction> { EmailAction.None };
var emailActivity = new List<EmailActivity>
{
new EmailActivity { Activity = new List<MemberActivity>{ new MemberActivity { Action = EmailAction.None } }, EmailAddress = "a" },
new EmailActivity { Activity = new List<MemberActivity>{ new MemberActivity { Action = EmailAction.Click } }, EmailAddress = "b" }
};
// Example with Any but All can be used as well
var activityEmailAddresses = emailActivity
.Where(x => x.Activity.Any(_ => _.Action.HasValue && activityTypes.Contains(_.Action.Value)))
.Select(x => x.EmailAddress)
.ToArray();
// Result is [ "a" ]

How to set flag in one list for the Id's which match with the Id's in another list using Lambda expression c#

I have two lists classes
public class class1{
public Int Id { get; set; }
public Bool Flag{ get; set; }
}
public class class2{
public Int Id { get; set; }
}
Now i have List<class1> and List<class2>,
Now i have to update Flag property to true in List<class1> for only those Ids which match with the Id's present in List<class2> using lambda expression c#.Don't want to use foreach.
using lambda expression. Don't want to use foreach.
That's usually a silly requirement and a hallmark that you're not really familiar with C#, Linq or performance analysis. You have a collection whose elements you want to modify, so you should use foreach().
If you're trying out functional programming, then you should treat the list elements as immutable and project into a new collection.
The first part of your problem, looking up which list elements to modify based on a presence of one of their properties in another collection's elements' properties, is trivial:
var elementsToModify = list1.Where(l1 => list2.Any(l2 => l2.Id == l1.Id));
Now with a foreach(), this'll be simple:
foreach (var l1 in elementsToModify)
{
l1.Flag = true;
}
Or, even denser (not that less code equals more performance):
foreach (var l1 in list1.Where(l1 => list2.Any(l2 => l2.Id == l1.Id)))
{
l1.Flag = true;
}
So, there's your code. But you didn't want to use foreach(). Then you need to project into a new collection:
var newList1 = list1.Where(l1 => list2.Any(l2 => l2.Id == l1.Id))
.Select(l1 => new Class1
{
Id = l1.Id,
Flag = true,
})
.ToList();
There you have it, a List<Class1> with only flagged items. Optionally you could use this list in a foreach() to update the original list1. Oh, wait.
The below solution does not use the classical "for each", but is compiled to one under the hood. If that's not what you meant, then please explain what you are trying to achieve. Using for each in this example is a good approach. One could also use while or for loops, but is it really what's being asked here?
Object definition:
public class MyObject
{
public int Id { get; set; }
public bool Flag { get; set; }
}
List initialization:
var list = new List<MyObject>()
{
new MyObject() { Id= 1 },
new MyObject() { Id= 2 },
new MyObject() { Id= 3 },
new MyObject() { Id= 4 }
};
var list2 = new List<MyObject>()
{
new MyObject() { Id= 2 },
new MyObject() { Id= 4 }
};
Code:
list.ForEach(el => el.Flag = list2.Any(el2 => el2.Id == el.Id));
EDIT:
An example with a while loop (a bit nasty to do it this way):
int i = -1;
int numberOfElements = list.Count;
while (++i < numberOfElements)
{
list[i].Flag = list2.Any(el => el.Id == list[i].Id);
}
I guess you can write a for loop yourself...

Custom OrderBy in Linq with singly linked entity list

I'm trying to come up with an efficient solution to be able to query the entity list and order it correctly. I have created a singly linked list type structure in SQL DB schema. I am using GUID as my IDs but for simplicity, I'll use int here. I could solve this problem easily by having a SortOrder column on the DB but because of other requirements, this is how I have to implement this table.
I have a table structure that looks like the following entity model:
public class Record
{
public int ID;
public string Name;
public int? ChildID; //References the next record
}
My initial thought is to create a partial class like the following:
public partial class Record
{
public int SortOrder
{
get
{
//query table and loop through the entire list building it from
//the bottom and keeping count of the integer sort order and return
//the one specific to this record
}
}
}
However, this seems very inefficient to have to query the entire list every time and iterate through to find the SortOrder. Is there anything else I can leverage like a custom OrderBy function or anything? I'm trying sort by the order that would be created when iterating a building the list. For instance, the record with ChildID = null, is the last one in the list, since it does not have a child. I'll start with that record, then get the next one above it that references the previous as its ChildID and go until there is no more in the list that has a reference to ID, which should be when the list is complete and ordered correctly. No two records have the same ChildID.
If I had the following 3 records in a list,
ID = 3, Name = "Apple", ChildID = 6,
ID = 54, Name = "Orange", ChildID = 3,
ID = 6, Name = "Banana", ChildID = null
Then I would expect to get Orange, Apple, Banana, in that order.
One way to do it would be to write a method that will return a list in sorted order. You would first find the record with ChildId == null, add it to the results list, and then continue to search for items where item.ChildId == previousItem.Id, and then insert them at the beginning of the list:
private static IEnumerable<Record> OrderRecords(IReadOnlyCollection<Record> records)
{
// "Exit fast" checks
if (records == null) return null;
if (records.Count < 2) return records.ToList();
// Get last record and add it to our results
Record currentRecord = records.Single(r => r.ChildID == null);
var results = new List<Record> {currentRecord};
// Keep getting the parent reference to the previous record
// and insert it at the beginning of the results list
while ((currentRecord = records.SingleOrDefault(r =>
r.ChildID == currentRecord.ID)) != null)
{
results.Insert(0, currentRecord);
}
return results;
}
In use, this would look something like:
private static void Main()
{
var records = new List<Record>
{
new Record {ID = 3, Name = "Apple", ChildID = 6},
new Record {ID = 54, Name = "Orange", ChildID = 3},
new Record {ID = 6, Name = "Banana", ChildID = null}
};
var sortedRecords = OrderRecords(records);
Console.WriteLine(string.Join(", ", sortedRecords.Select(r => r.Name)));
Console.Write("\nPress any key to exit...");
Console.ReadKey();
}
Output
Given that the record ID order is random, and assuming that the List you are ordering is complete, or that you won't run out of memory/time if you have to scan the entire table to order the list, I think the best you can do is compute the depth for a Record and cache the results:
I am using the List as the table, but you could use the table instead if the list you want to order is incomplete:
public partial class Record {
static Dictionary<int, int> depth = new Dictionary<int, int>();
public int Depth(List<Record> dbTable) {
int ans = 0;
var working = new Queue<int>();
var cur = this;
do {
if (depth.TryGetValue(cur.ID, out var curDepth)) {
ans += curDepth;
break;
}
else {
working.Enqueue(cur.ID);
cur = dbTable.FirstOrDefault(r => r.ChildID == cur.ID);
if (cur != null)
++ans;
}
} while (cur != null);
var workAns = ans;
while (working.Count > 0) {
var workingID = working.Dequeue();
depth.Add(workingID, workAns);
--workAns;
}
return ans;
}
}
Update: I re-wrote the code to use a specific queue; my first version was recursive and that was straightforward but risked overflowing the stack and my second version didn't cache the intermediate results when following the linked list which wasn't very efficient. Using a queue of the intermediate IDs ensures I only follow a particular chain depth once.
Now that you have a Depth method, sorting is easy:
var ans = work.OrderBy(w => w.Depth(work));
The best algorithm for this task is to prepare a fast lookup data structure (like Dictionary) of Record by ChildID. Then the ordered result can be produced backwards starting with ChildID = null and using the record ID to find the previous record.
Since the hash lookup time complexity is O(1), the time complexity of the algorithm is linear O(N) - the fastest possible.
Here is the implementation:
static Record[] Ordered(IEnumerable<Record> records)
{
var recordByNextId = records.ToDictionary(e => e.ChildID.Wrap());
var result = new Record[recordByNextId.Count];
int? nextId = null;
for (int i = result.Length - 1; i >=0; i--)
nextId = (result[i] = recordByNextId[nextId]).ID;
return result;
}
The explanation of e.ChildID.Wrap() custom extension method. I wish I can use simply e.ChildID, but the BCL Dictionary class throws annoying exception for null key. To overcome that limitation in general, I use a simple wrapper struct and "fluent" helper:
public struct ValueWrapper<T> : IEquatable<ValueWrapper<T>>
{
public readonly T Value;
public ValueWrapper(T value) => Value = value;
public bool Equals(ValueWrapper<T> other) => EqualityComparer<T>.Default.Equals(Value, other.Value);
public override bool Equals(object obj) => obj is ValueWrapper<T> other && Equals(other);
public override int GetHashCode() => EqualityComparer<T>.Default.GetHashCode(Value);
public static implicit operator ValueWrapper<T>(T x) => new ValueWrapper<T>(x);
public static implicit operator T(ValueWrapper<T> x) => x.Value;
}
public static class ValueWrapper
{
public static ValueWrapper<T> Wrap<T>(this T value) => new ValueWrapper<T>(value);
}

Method return type list<string> or generic method?

I would like that the method has a specfic return type, but somehow I cannot make it work.
I have an XML structure:
<Notifications>
<Alerts>
<Max>3<Max/>
<Med>2<Med/>
<Min>1<Min/>
</Alerts>
</Notifications>
I would like to get out the values of Max, Med, Min. But the main point is I DO NOT want any foreach loops at all, I only want that the method has a List<string> return type, or even better to make a generic return type.
The point is, I don't have any custom class (and I don't want to have it) that I would fill its properties.
This is what I have some far, but I got an error on the "List()" annonymus method:
Here it returns:
List<string> items = GetAlerts();
//method to read:
public List<string> GetAlerts()
{
return xmlDoc1.Descendants("Notifications").Elements("Alerts").Select(s => new
{
MAX = s.Element("Max").Value,
MED = s.Element("Med").Value,
MIN = s.Element("Min").Value
}).ToList(); //error on this line
}
----------
And how would it look that this method about would be a generic return type? This is NOT OK:
Here it returns:
List<object> items = setBLL2.GetAlert<List<object>>();
public T GetAlert<T>() where T:class
{
return (T)xmlDoc1.Descendants("Notifications").Elements("Alerts").Select(s => new
//ERROR up here before (T
{
MAX = s.Element("Max").Value,
MED = s.Element("Med").Value,
MIN = s.Element("Min").Value
}).ToList();
}
The error message is:
Cannot convert type 'System.Collections.Generic.List' to 'T'
You cannot transfer anonymous types across method boundaries (in your case, as the generic type of the List<T> that is being returned from your method).
You should define a class or struct with Max, Med, and Min as properties, and initialize a list of its instances from your method.
public class Alert
{
public string Max { get; set; }
public string Med { get; set; }
public string Min { get; set; }
}
public IList<Alert> GetAlerts()
{
return (xmlDoc1.Descendant("Notifications").Elements("Alerts").Select(s =>
new Alert
{
Max = s.Element("Max").Value,
Med = s.Element("Med").Value,
Min = s.Element("Min").Value
}).ToList();
}
Edit: As an alternative, you could return a list of dictionaries mapping the property names to their values:
public IList<Dictionary<string,string>> GetAlerts()
{
return (xmlDoc1.Descendant("Notifications").Elements("Alerts").Select(s =>
new Dictionary<string,string>
{
{ "Max", s.Element("Max").Value },
{ "Med", s.Element("Med").Value },
{ "Min", s.Element("Min").Value }
}).ToList();
}
You could them access your values using code like:
string firstMin = alerts[0]["Min"];
Try this instead:
void Main()
{
List<string> alerts =
XDocument.Parse(Data)
.Descendants("Alerts")
.Elements()
.Select (nd => nd.Value)
.ToList();
alerts.ForEach(al => Console.WriteLine ( al ) ); // 3 2 1 on seperate lines
}
// Define other methods and classes here
const string Data = #"<?xml version=""1.0""?>
<Notifications>
<Alerts>
<Max>3</Max>
<Med>2</Med>
<Min>1</Min>
</Alerts>
</Notifications>";
Unfortunately, I don't know how to get MAX, MED, and MIN returned in separate entries in a List within the LINQ query. What you can do though, is have your LINQ query put MIN, MED, and MAX into a List object, and then return the first row.
public IList<Alert> GetAlerts()
{
var xmlDoc1 = XDocument.Parse(XML, LoadOptions.None);
var entries = xmlDoc1.Descendants("Notifications").Elements("Alerts").Select(s => new
List<string> {
s.Element("Max").Value,
s.Element("Med").Value,
s.Element("Min").Value
}).ToList();
// Make sure we don't get an ArgumentOutOfRangeException
if (entries.Count > 0)
{
return entries[0];
}
else
{
return new List<string>();
}
}
P.S. - Your end tags for MIN, MAX, and MED are formatted incorrectly, the '/' should be before the name, not after.

Categories