How to remove duplicates from the list in C# - c#

Please help me to fix this issue. My dropdown list looks something like this mentioned below.
Client
Contractor,Contractor,Contractor,Manager
Contractor,Manager
Manager
Operator
Viewer
I want to remove the duplicates and my output should be like :
Client
Contractor
Manager
Operator
Viewer
This is my code mentioned below:
Property:
public List<string> TeamRoleNames => TeamRoleUids.Select(MainRoles.GetRoleName).ToList();
Display Method:
{
result += " ; TeamRoleNames=" + this.TeamRoleNames;
}
GetRole Method:
{
string roleName;
if (RoleNameByUid.TryGetValue(roleUid, out roleName))
{
return roleName;
}
return null;
}
I have tried with Distinct Method mentioned below, But did not work like the output what I wanted!
public List<string> TeamRoleNames => TeamRoleUids.Select(MainRoles.GetRoleName).Distinct().ToList();
How can I fix this? Can anyone help?

Having elements comma separated require you to split them first to have an homogenous collection then do the distinct
// get the comma separated values out as 1 value each
// for that you can use split, remove empty and select many
// which will return as a single level list (flat)
var result = TeamRoleUids.SelectMany(o => o.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)).Distinct().ToList();

Consider converting the list to a set (hashset) since sets as a data structure doesn't allow duplicates.
More about hashsets form official documentation.
So, the solution would be similar to the following:
var hashSet = new HashSet<YourType>(yourList);
example:
var hashSet = new HashSet<string>(TeamRoleUids);
then converting it back toList() will remove duplicates.

If you have already tried Distinct and it hasn't worked, then you could do the following;
Split your string list to a List<string>
List<string> result = TeamRoleNames.Split(',').ToList();
Then when you're adding them to the dropdwon, check to see if the role is already in the dropdown. If so, move on, else add to the dropdown.
So something like
foreach(var role in this.TeamRoleNames)
{
if(!result.contains(role))
result += " ; TeamRoleNames=" + role;
}

You can use SelectMany to flatten a enumeration containing a nested enumeration. Here, we create the nested enumeration by splitting the string at the commas:
string[] input = {
"Client",
"Contractor,Contractor,Contractor,Manager",
"Contractor,Manager",
"Manager",
"Operator",
"Viewer"
};
var roles = input
.SelectMany(r => r.Split(','))
.Distinct()
.OrderBy(r => r)
.ToList();
foreach (string role in roles) {
Console.WriteLine(role);
}
prints
Client
Contractor
Manager
Operator
Viewer

Related

Simple code to get specific item from a list

I have a list of a tags. I want to get an a tag which contains a string.
I used the below code and everything work fine.
string mainLink = "";
List<HtmlNode> dlLink = new List<HtmlNode>();
dlLink = doc.DocumentNode.SelectNodes("//div[#class='links']//a").ToList();
foreach (var item in dlLink) {
if (item.Attributes["href"].Value.Contains("prefile"))
{
mainLink = item.Attributes["href"].Value;
}
}
but I want to write a simple code
var dlLink = doc.DocumentNode.SelectNodes("//div[#class='link']//a").ToList().Where(x => x.Attributes["href"].Value.Contains("prefile")).ToList().ToString();
But it does not work and I get nothing.
Your foreach is setting mainLink string, but your linq chain is using ToString on a List result.
Converting your code, you will have something like this:
mainLink = doc.DocumentNode.SelectNodes("//div[#class='links']//a")
.Where(item => item.Attributes["href"].Value.Contains("prefile"))
.Select(item => item.Attributes["href"].Value)
.Last();
I used Select to get only the href values, and getting the last as your foreach did, maybe you need to validate this last step, use a LastOrDefault, First, etc.
You can also use the Last or First instead of the Where condition:
mainlink = doc.DocumentNode.SelectNodes("//div[#class='links']//a")
.Last(item => item.Attributes["href"].Value.Contains("prefile"))
.Attributes["href"].Value;

Match sections of a List, and Replace if both exist

I've got dates from separate countries within a single List<>. I'm trying to get two records that contain the same characters before the second comma, and replace BOTH of those items with a new one.
Example:
From This:
18/04/2014,Good Friday,England and Wales
18/04/2014,Good Friday,Scotland
Into this:
18/04/2014,Good Friday,"England, Wales and Scotland"
Please note there may be multiple scenarios within the list like the above example. I've managed to get everything before the second Comma with:
splitSubstring = line.Remove(line.LastIndexOf(','));
I've tried the below, but it's clearly flawed since it won't delete both the records even if it does find a match:
foreach (var line in orderedLines)
{
if (splitSubstring == line.Remove(line.LastIndexOf(',')))
{
//Replace if previous is match here
}
splitSubstring = line.Remove(line.LastIndexOf(','));
File.AppendAllText(correctFile, line);
}
I would suggest parsing it into a structure you can work with e.g.
public class HolidayInfo
{
public DateTime Date { get; set; }
public string Name { get; set; }
public string[] Countries { get; set; }
};
And then
string[] lines = new string[]
{
"18/04/2014,Good Friday,England and Wales",
"18/04/2014,Good Friday,Scotland"
};
// splits the lines into an array of strings
IEnumerable<string[]> parsed = lines.Select(l => l.Split(','));
// copy the parsed lines into a data structure you can write code against
IEnumerable<HolidayInfo> info = parsed
.Select(l => new HolidayInfo
{
Date = DateTime.Parse(l[0]),
Name = l[1],
Countries = l[2].Split(new[] {",", " and " }, StringSplitOptions.RemoveEmptyEntries)
});
...etc. And once you have it in a helpful data structure you can begin to develop the required logic. The above code is just an example, the approach is what you should focus on.
I ended up using LINQ to pull apart the List and .Add() them into another based on an if statement. LINQ made it nice and simple.
//Using LINQ to seperate the two locations from the list.
var seperateScotland = from s in toBeInsertedList
where s.HolidayLocation == scotlandName
select s;
var seperateEngland = from e in toBeInsertedList
where e.HolidayLocation == engAndWales
select e;
Thanks for pointing me to LINQ

C# scan list against master list for missing items

I have a master list that has the values for tables of a database I know to be correct:
masterList: List<string>(){ "business", "customer", "location", "employee", etc}
And I've queried a new database that is supposed to be identical. My test will tell me if I have any errors in the scripts my team has made to make this new DB. tablesList is supposed to the be the return of my query:
tablesList: List<string>(){ "business", "customer", "location", "employee", etc}
So in practice they are supposed to be the same, but to test errors, I want to compare the tablesList against the masterList to make sure all needed tables are there. As a copy of this process, I'm also reversing the search, in case there are any extra tables that are not there on the masterList.
Question: How do I compare a list against a master list, and return items that don't match up?
I am using Visual Studio 2017 with c# .net Core 2.0.
Here is what I've been trying so far:
var errorsList = new List<string>();
tablesList = QuerySchemaForTables();
masterList = GrabMasterTableList();
foreach(var item in masterList)
errorsList.Add(tablesList.Where(x => x.Contains(item)));
But with this, I'm getting the error:
cannot convert from IEnumerable to string
You can get the two directions of errors using LINQ. No need for the loop:
var missingInMasterList = tableList.Where(x => !masterList.Contains(x)).ToList();
var missingInTableList = masterList.Where(x => !tableList.Contains(x)).ToList();
Are you looking for something like that;
var errorList = tableList.Where(x => !masterList.Contains(x));
You can capture the differences using .Except(), which is one of the IEnumerable set operations:
var missingTables = masterList.Except(tablesList);
var extraTables = tablesList.Except(masterList);
Then, to create your error message, you can join the items in these IEnumerables with a comma into a single string using string.Join():
var errorMessage = new StringBuilder();
if (missingTables.Any())
{
errorMessage.AppendLine("Missing Tables: " + string.Join(", ", missingTables));
}
if (extraTables.Any())
{
errorMessage.AppendLine("Extra Tables: " + string.Join(", ", extraTables));
}
Then you can output your results by checking the length of errorMessage to determine if any errors were encountered:
if (errorMessage.Length > 0)
{
Console.WriteLine(errorMessage.ToString());
}
else
{
Console.WriteLine("No extra or missing tables detected");
}
I think better to use is Except() as follows
var MasterList = new List<string> { "business", "customer", "location", "employee"};
var ChildList = new List<String> { "customer", "location", "employee" };
var filter = MasterList.Except(ChildList);
This will values those are not in ChildList.You can also do vice versa.
To find all items that are in the tablesList but not in the masterList use .Contains:
var errorsList = tableList.Where(x => !masterList.Contains(x));
But I recommend you use a HashSet<String> for masterList so search for an item in it will be in O(1) instead of O(n):
var masterCollection = new HashSet<String>(GrabMasterTableList());
var errorsList = tableList.Where(x => !masterCollection.Contains(x));
As for the problem with your code as you posted:
foreach(var item in masterList)
errorsList.Add(tablesList.Where(x => x.Contains(item))); // <-- error
As the error points out, Linq's .Where returns an IEnumerable<T> whereas .Add expects a single item of the type of the collection, which in this case is a single string. You could use .AddRange instead but I think a better use all together is what I wrote above.
Your code is presently trying to add an IEnumerable to a List.
If you want to add all the matches you should AddRange instead.
https://msdn.microsoft.com/en-us/library/z883w3dc(v=vs.110).aspx
var errorsList = new List<string>();
tablesList = QuerySchemaForTables();
masterList = GrabMasterTableList();
foreach(var item in masterList)
errorsList.AddRange(tablesList.Where(x => x.Contains(item)));

C# How can I do a string replace method on the collection?

I have the following method which return a collection so I can then build a dropdown menu from in MVC. The problem is, I want to replace a substring within the values in the collection but I am not sure how to do so in C# (I'm a classic vb guy).
public class RolesManagement
{
public static IEnumerable<string> BuildRoles(string DesiredRole)
{
var UserRoles = Roles.GetAllRoles();
IEnumerable<string> roles;
roles = UserRoles.Where(x => x.Contains("Sub")).Except(rejectAdmin);
return roles;
}
}
The above has been simplified for brevity. Each role in the roles collection looks like this:
SubUser
SubAdmin
SubManager.
I simply want to return
User
Admin
Manager
What would be the best approach for this in C# please?
My guess is that I have to do a foreach and replace the substring on each loop and the re-populate the value before moving to the next item.
If you could provide a code sample that would be great as I seem to still me tripping over syntax issues.
Much appreciated!
You could do that with LINQ.
roles = UserRoles
.Where(x => x.Contains("Sub"))
.Except(rejectAdmin)
.Select(x => x.Replace("Sub", ""));
Edit: Note that this method simply replaces all occurrences of the string "Sub" in all of the strings in roles. It's a very broad stroke and you may need to use a different lambda function if you only want to do the replacements you mentioned. See Romoku's post for help with that.
I would say if the roles are not going to change very often then make a mapping Dictionary.
public class RolesManagement
{
private static readonly Dictionary<string,string> RoleMapping =
new Dictionary<string,string>
{
{"SubUser", "User" },
{"SubManager", "Manager" },
{"SubAdmin", "Admin" }
};
public static IEnumerable<string> BuildRoles(string DesiredRole)
{
var UserRoles = Roles.GetAllRoles();
IEnumerable<string> roles;
roles = UserRoles.Where(x => x.Contains("Sub")).Except(rejectAdmin);
return roles.Select(r => RoleMapping.ContainsKey(r) ? RoleMapping[r] : r);
}
}
Or just store them inside the database as a FriendlyName column.
this will return all items start with sub, with the sub removed
IEnumerable<string> = UserRoles
.Where(x => x.StartsWith("sub"))
.Select(y => y.Remove(0,3));
public static IEnumerable<string> GetCorrectedRoleNames(IEnumerable<string> roles)
{
List<string> correctedRoles = new List<string>();
foreach (string role in roles)
correctedRoles.Add(Regex.Replace(role, "Sub", string.Empty));
return correctedRoles.AsEnumerable();
}
public static IEnumerable<string> BuildRoles(string DesiredRole)
{
var UserRoles = Roles.GetAllRoles();
IEnumerable<string> roles;
roles = UserRoles.Where(x => x.Contains("Sub")).Except(rejectAdmin).Select(x=>x.Replace("Sub","");
return roles;
}
i supposed that x is string variable if not you have to do this replace on string collection
If I understand your problem right, you need to remove the occurence of "Sub" in a string. You can do additional checks if you have other conditions , like "Sub" is at the beginning of the string, case-insensitivity, etc.
One of the ways is to find the index of the string to be removed and then remove.
var s = "SubUser";
var t = s.Remove(s.IndexOf("Sub"), "Sub".Length);
Alternatively, you can also replace the string to be removed with an empty string.
var t = s.Replace("Sub", "");
Hope that helps.
Try this
List<string> x = new List<string>();
x.Add("SubUser");
x.Add("SubAdmin");
x.Add("SubManager");
var m = x.Select( y => y.Replace("Sub", ""));

How to compare values in 2 lists

I have a list as Users = new List<string>();
I have another List, List<TestList>();
UsersList = new List<string>();
I need to compare the values from Users with TestList.Name. If the value in TestList.Name is present in Users, I must must not add it to UsersList, else, I must add it to UsersList.
How can I do that using Linq?
It looks to me like you want:
List<string> usersList = testList.Select(x = > x.Name)
.Except(users)
.ToList();
In other words, "use all the names of the users in testList except those in users, and convert the result to a List<string>".
That's assuming you don't have anything in usersList to start with. If usersList already exists and contains some values, you could use:
usersList.AddRange(testList.Select(x = > x.Name).Except(users));
Note that this won't take account of the existing items in usersList, so you may end up with duplicates.
Do a loop on you list - for example :
foreach (string s in MyList)
{
if (!MyList2.Contains(s))
{
// Do whatever ; add to the list
MyList2.Add(s);
}
}
..that's how I interpreted you question

Categories

Resources