How to remove identical items from List<string>? - c#

I have a List in which I select users from db each time a sql query runs with certain value and selects one user in the time thus I cannot limit identical users in sql.
I have list with:
list[0] = "jerry"
list[1] = "tom"
list[2] = "jerry"
I want any (first or last doesn't matter in my case) to be removed from the list.
Thanks

IEnumerable<string> uniqueUsers = list.Distinct();
You can also use a HashSet:
HashSet<string> uniqueUsers = new HashSet<string>(list);

LINQ can solve this:
List<string> names = new List<string> { "Tom", "Jerry", "Tom" };
IQueryable<string> distinctItems = names.Distinct();
If you want a list type, simply call ToList():
distinctItems.ToList();
Here's an example from the MSDN.
EDIT: Non-LINQ Example (using Contains() from the List class):
List<string> names = new List<string> { "Tom", "Jerry", "Tom" };
List<string> distinctNames = new List<string>();
foreach (var name in names)
{
if (!distinctNames.Contains(name))
{
distinctNames.Add(name);
}
}

You can use the Distinct() LINQ extension.
var list = new List<string> { "Tom", "Jerry", "Tom" };
var uniqueList = list.Distinct();

Using Distinct, as suggested in the other answers, will leave your original list intact and return a separate IEnumerable<> sequence containing the distinct items from your list.
An alternative would be to remove duplicates from your original list directly, using RemoveAll:
var temp = new HashSet<string>();
yourList.RemoveAll(x => !temp.Add(x));

you can use list.distinct();

Related

Split list of strings by list of key words [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have a list of strings
e.g.{"apple.txt", "orange.sd.2.txt", "apple.2.tf.txt", "orange.txt"}
and another list of strings to group the first list
e.g. {"apple", "orange"}
so that the first list is split into a list of lists and looks like this:
{{"apple.txt", "apple.2.tf.txt"},{"orange.txt", "orange.sd.2.txt"}}
How can I achieve this with linq?
How about this:
var groupedList = firstList.GroupBy(x => secondList.Single(y => x.Name.Contains(y)));
You could group the elements of each of the original list by all possible keys using Split, SelectMany, and GroupBy with an anonymous type:
var list = new List<string> { "apple.txt", "orange.sd.2.txt", "apple.2.tf.txt", "orange.txt" };
var groups = list
.SelectMany(element => element
.Split('.')
.Select(part => new { Part = part, Full = element }))
.GroupBy(entry => entry.Part);
Now you can select the groups you want to keep using Where, and convert the results into the nested lists using Select and ToList:
var keys = new List<string> { "apple", "orange" };
var result = group
.Where(group => keys.Contains(group.Key))
.Select(group => group
.Select(entry => entry.Full)
.ToList())
.ToList();
N.B. Elements of the original list which do not contain any of the specified keys will not appear in the results, and elements which contain more than one of the specified keys will appear more than once in the result.
Edit: As #NetMage noted, I've made an incorrect assumption about splitting strings - here's another version, although it's O(m * n):
var result = keys
.Select(key => list.Where(element => element.Contains(key)).ToList())
.ToList();
This is one simple way to do it. There is many ways and this will include duplicated key as the comment i made on your question. If many key match the same data the grouping will include the copies.
// have the list of keys (groups)
var keyList = new List<string>() {"apple", "orange"};
// have the list of all the data to split
var dataToSplit = new List<string>()
{
"apple.txt",
"apple.2.tf.txt",
"orange.txt",
"orange.sd.2.txt"
};
// now split to get just as desired you select what you want for each keys
var groupedData = keyList.Select(key => dataToSplit.Where(data => data.Contains(key)).ToList()).ToList();
// groupedData is a List<List<string>>
A second option to get the values maybe in a more "object" fashion is to use anonymous. specially good if you will do lots of manipulation and it's more "verbiose" in the code. But if you are new to this i do NOT recommend that approach but anyhow this is it.
// have the list of keys (groups)
var keyList = new List<string>() {"apple", "orange"};
// have the list of all the data to split
var dataToSplit = new List<string>()
{
"apple.txt",
"apple.2.tf.txt",
"orange.txt",
"orange.sd.2.txt"
};
// create the anonymous
var anonymousGroup = keyList.Select(key =>
{
return new
{
Key = key,
Data = dataToSplit.Where(data => data.Contains(key)).ToList()
}
});
// anonymousGroup is a List<A> where keeping the order you should access all data for orange like this
var orangeGroup = anonymousGroup.FirstOfDefault(o=> o.Key = "orange"); // get the anonymous
var orangeData = orangeGroup.Data; // get the List<string> for that group
A third way with less complexity than O(m*n). The trick is to remove from the collection the data as you go to reduce the chance to recheck over item already processed. This is from my codebase and it's an extension for List that simply remove item from a collection based on a predicate and return what has been removed.
public static List<T> RemoveAndGet<T>(this List<T> list, Func<T, bool> predicate)
{
var itemsRemoved = new List<T>();
// iterate backward for performance
for (int i = list.Count - 1; i >= 0; i--)
{
// keep item pointer
var item = list[i];
// if the item match the remove predicate
if (predicate(item))
{
// add the item to the returned list
itemsRemoved.Add(item);
// remove the item from the source list
list.RemoveAt(i);
}
}
return itemsRemoved;
}
Now with that extension when you have a list you can use it easily like this :
// have the list of keys (groups)
var keyList = new List<string>() {"apple", "orange"};
// have the list of all the data to split
var dataToSplit = new List<string>()
{
"apple.txt",
"apple.2.tf.txt",
"orange.txt",
"orange.sd.2.txt"
};
// now split to get just as desired you select what you want for each keys
var groupedData = keyList.Select(key => dataToSplit.RemoveAndGet(data => data.Contains(key))).ToList();
In that case due to the order in both collection the first key is apple so it will iterate the 4 items in dataToSplit and keep only 2 AND reducing the dataToSplit collection to 2 items only being the one with orange in them. On the second key it will iterate only over 2 items which will make it faster for this case. Typically this method will be as fast or faster than the first 2 ones i provided while being as clear and still make use of linq.
You can achieve this using this simple code:
var list1 = new List<string>() {"apple.txt", "orange.sd.2.txt", "apple.2.tf.txt", "orange.txt"};
var list2 = new List<string>() {"apple", "orange"};
var result = new List<List<string>>();
list2.ForEach(e => {
result.Add(list1.Where(el => el.Contains(e)).ToList());
});
Tuples to the rescue!
var R = new List<(string, List<string>)> { ("orange", new List<string>()), ("apple", new List<string>()) };
var L = new List<string> { "apple.txt", "apple.2.tf.txt", "orange.txt", "orange.sd.2.txt" };
R.ForEach(r => L.ForEach(l => { if (l.Contains(r.Item1)) { r.Item2.Add(l); } }));
var resultString = string.Join("," , R.Select(x => "{" + string.Join(",", x.Item2) + "}"));
You can build R dynamically trivially if you need to.

Sorting an array of List<string> using linq

How do I sort an array of List<string> by length of string using Linq? The efficiency of execution does not matter.
List<string>[] col = new List<string>[]
{
new List<string>(){"aaa", "bbb"},
new List<string>(){"a", "b", "d"},
new List<string>(){"xy","sl","yy","aq"}
}; //Each string in a list of strings of a particular array element has equal length.
After sorting should produce
{"a", "b", "d"}, {"xy","sl","yy","aq"}, {"aaa", "bbb"}
This should work:
var ordered = col.OrderBy(x => x[0].Length).ToList();
Try it out here.
If you want to sort out existing col list (i.e. in place sorting):
col.Sort((left, right) = > left[0].Length.CompareTo(right[0].Length));
It's not Linq, however.
Order by the length of the first item in each member list:
var ordered = col.OrderBy(c => c.First().Length);
Fiddle
Or if it should be able to handle empty lists and nulls:
var ordered = col.OrderBy(c => c.FirstOrDefault()?.Length);
Fiddle

LINQ to return subset of data grouped into an anonymous type

I have a list of people and I'd like to use LINQ (query syntax) to get an anonymous type containing all the firstnames and all the second names.
If I were to use a foreach:
var firstNames = new HashSet<string>();
var secondNames = new HashSet<string>();
foreach (Person p in ListOfPersons)
{
firstNames.Add(p.firstName);
secondNames.Add(p.secondName);
}
What is an equivalent and efficient LINQ statement that returns an anonymous type? Eg, allNames.FirstNames and allNames.SecondNames.
EDIT: when I say efficient, I mean that it loops over the ListOfPersons once, as in the foreach example above.
EDIT 2: the names should be distinct; I changed the List<> to HashSet<> in the foreach example
If you don't want to iterate ListOfPersons twice the only way I see it in linq is
var firstNames = new List<string>();
var secondNames = new List<string>();
persons.Aggregate(Tuple.Create(firstNames, secondNames), (tuple, person) =>
{
tuple.Item1.Add(person.firstName);
tuple.Item2.Add(person.secondName);
return tuple;
});
but I think foreach is much better.
Try this:
var AnonList = ListOfPersons.Select(x=> new {firstname = x.firstName, secondname = x.secondName});
var nameList = from l in ListOfPersons
select new { FirstName = l.Firstname, Surname = l.Surname};
How about this:
var allNames = new
{
firstNames = new List<string>(),
secondNames = new List<string>()
};
listOfPersons.ForEach( p =>
{
allNames.firstNames.Add( p.firstName );
allNames.secondNames.Add( p.secondName );
} );
Counter intuitively, this is the most efficient:
var names = new
{
firstNames = ListOfPersons.Select(x => x.firstName).Distinct().ToList(),
secondNames = ListOfPersons.Select(x => x.secondName).Distinct().ToList(),
};
It turns out that iterating over the list twice is more efficient that concocting a method to iterate over it once as many more temporary variables are created. I've done speed tests on this kind of thing and iterating over the list multiple times wins.

Lambda multiple conditions to validate list in list of object?

I have a list composed of the following items
List<person> lst = new List<person>()
lst.Add(new person(){ name="abc", age="24" });
lst.Add(new person(){ name="xyz", age="25" });
lst.Add(new person(){ name="zxc", age="28" });
lst.Add(new person(){ name="wer", age="38" });
List<string> strlst = new List<string>();
strlst.Add("abc");
strlst.Add("zxc");
right now i am validating the list
lst.Exists(a => strlst.Contains(a.name))
How can I validate the entire list if it contains 'abcandxyz`?
If you find the Intersect of the two names you get the names common to both lists. If that intersection is the same size as your string list then it means all of them are in the intersection, which means they're all in the other list.
return list.Select(person => person.name)
.Intersect(strlst).Count() == strlst.Count();
Correct me if I'm wrong, but I believe your original intent is to validate against your lst variable, not strlst. If it's for strlist, then I believe #LosManos's answer is a very good approach. If you are trying to validate List<person> lst with the values in strlst then you should do the following:
var allNames = lst.Select(l => l.name);
var containsAll = strlist.All(s => allNames.Contains(s));
This way would also ensure that you could do it correctly even if you have more than two things you need to compare on.

Remove items from one list in another

I'm trying to figure out how to traverse a generic list of items that I want to remove from another list of items.
So let's say I have this as a hypothetical example
List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();
I want to traverse list1 with a foreach and remove each item in List1 which is also contained in List2.
I'm not quite sure how to go about that as foreach is not index based.
You can use Except:
List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();
List<car> result = list2.Except(list1).ToList();
You probably don't even need those temporary variables:
List<car> result = GetSomeOtherList().Except(GetTheList()).ToList();
Note that Except does not modify either list - it creates a new list with the result.
You don't need an index, as the List<T> class allows you to remove items by value rather than index by using the Remove function.
foreach(car item in list1) list2.Remove(item);
In my case I had two different lists, with a common identifier, kind of like a foreign key.
The second solution cited by "nzrytmn":
var result = list1.Where(p => !list2.Any(x => x.ID == p.ID && x.property1 == p.property1)).ToList();
Was the one that best fit in my situation.
I needed to load a DropDownList without the records that had already been registered.
Thank you !!!
This is my code:
t1 = new T1();
t2 = new T2();
List<T1> list1 = t1.getList();
List<T2> list2 = t2.getList();
ddlT3.DataSource= list2.Where(s => !list1.Any(p => p.Id == s.ID)).ToList();
ddlT3.DataTextField = "AnyThing";
ddlT3.DataValueField = "IdAnyThing";
ddlT3.DataBind();
I would recommend using the LINQ extension methods. You can easily do it with one line of code like so:
list2 = list2.Except(list1).ToList();
This is assuming of course the objects in list1 that you are removing from list2 are the same instance.
list1.RemoveAll(l => list2.Contains(l));
You could use LINQ, but I would go with RemoveAll method. I think that is the one that better expresses your intent.
var integers = new List<int> { 1, 2, 3, 4, 5 };
var remove = new List<int> { 1, 3, 5 };
integers.RemoveAll(i => remove.Contains(i));
Solution 1 : You can do like this :
List<car> result = GetSomeOtherList().Except(GetTheList()).ToList();
But in some cases may this solution not work. if it is not work you can use my second solution .
Solution 2 :
List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();
we pretend that list1 is your main list and list2 is your secondry list and you want to get items of list1 without items of list2.
var result = list1.Where(p => !list2.Any(x => x.ID == p.ID && x.property1 == p.property1)).ToList();
As Except does not modify the list, you can use ForEach on List<T>:
list2.ForEach(item => list1.Remove(item));
It may not be the most efficient way, but it is simple, therefore readable, and it updates the original list (which is my requirement).
I think it would be quick to convert list A to a dictionary and then foreach the second list and call DictA.Remove(item) otherwise I think most solutions will cause many iterations through list A either directly or under the covers.
If the lists are small, it probably won't matter.
In case you have two different list with different DataModals
List<FeedbackQuestionsModel> feedbackQuestionsList = new();
List<EmployeesFeedbacksQuestionsModel> employeeQuestionsList = new();
var resultList = feedbackQuestionsList.Where(p => !employeeQuestionsList.Any(x => x.Question == p.Question)).ToList();
feedbackQuestionsList = resultList.ToList();
Here ya go..
List<string> list = new List<string>() { "1", "2", "3" };
List<string> remove = new List<string>() { "2" };
list.ForEach(s =>
{
if (remove.Contains(s))
{
list.Remove(s);
}
});

Categories

Resources