I have a List that has various derived classes. I may have something like this:
List<BaseClass> list = new List<BaseClass>() {
new Class1(),
new Class2(1),
new Class3(),
new Class2(2),
new Class4()
};
I am trying to use LINQ to semi-sort the list so that the natural order is maintained EXCEPT for Class2. All Class2 instances should be grouped together at the place that the first Class2 occurs. Here is what the output should be like:
List<BaseClass> list = new List<BaseClass>() {
new Class1(),
new Class2(1),
new Class2(2),
new Class3(),
new Class4()
};
I can't for the life of me figure out how to do this...
You can do it like this:
list = list
.TakeWhile(o => !(o is Class2))
.Concat(list.Where(o => o is Class2))
.Concat(
list.SkipWhile(o => !(o is Class2)).Where(o => !(o is Class2))
)
.ToList();
This will take all of the items until the first Class2 item, followed by all of the Class2 items, followed by all remaining non-Class2 items.
Related
Let's say I have the following:
public class Person
{
public string Name{get;set;}
public string Other{get;set;}
public string Other2{get;set;}
public int? Sequence{get;set;}
}
new Person("bob","other1","other2",1)
new Person("bob","other1","other2",2)
new Person("bob","other1","other2",3)
new Person("bob","other1","other2",4)
new Person("Alice","other1","other2")
new Person("Alice","other1","other2",1)
new Person("Alan","other1","other2",1)
new Person("Alan","other1","other2",2)
new Person("Alan","other1","other2",3)
new Person("Alex","other1","other2")
new Person("Alex","other1","other2",1)
new Person("Alex","other1","other2",2)
As shown some of the objects have sequence 1-n and some don't.
Could I use LINQ to pull objects by sequence from the given list?
Desired output would be:
Bob and all his related data where a sequence is there like 1,2,3,4 records
Alex 2 records as he only has sequences 1 and 2.
So the output would be another object by name and data by sequence.
new {Name="Bob", Data=new[]{
Other = "other"
Sequence = 1
Other2 = "Other2" //etc
}}
The sequence will always increment by 1 and be in order, but how many there might be is unknown.
If I have not made something clear just ask.
What I tried
I tried without using LINQ and looping through the list and processing each object and passing out a newly created object for each row using lots of if's.
I am just wondering if there is an easier way with LINQ although my way works it's ugly.
If I understand correctly:
var result = yourCollection
.Where(x => x.Sequence.HasValue)
.GroupBy(x => x.Name)
.Select(grp => new
{
Name = grp.Key,
Data = grp.Select(x => new
{
x.Other,
x.Sequence,
x.Other2
})
});
This assumes the list is already ordered by Sequence; if not, just add an .OrderBy(x => x.Sequence.Value) before the GroupBy.
I have collection of elements and one additional small collection as filter.
I need to separate it on 2 new collections by some filter. In my case it is first collection that contains some elements and another that doesn't.
There aren't items that doesn't exists out of that 2 new collections.
I did it like :
var collection1= baseCollection.Where(r => filterCollection.Contains(r.Property)).ToList();
var collection2= baseCollection.Where(r => !filterCollection.Contains(r.Property)).ToList();
But is there another, I hope more elegant way, to separate collection?
For me it looks like "I repeat myself", use almost the same code 2 times.
You can create a variable for the function - this way you will not "repeat yourself" (wouldn't use in this case, there are better options below, but still an option):
Func<YourClass,bool> filtering = (r) => filterCollection.Contains(r.Property);
var collection1 = baseCollection.Where(r => filtering(r));
var collection2 = baseCollection.Where(r => !filtering(r));
If your type of the collection overrides Equals and GetHashCode you can use Except:
var collection1 = baseCollection.Where(r => filterCollection.Contains(r.Property));
var collection2 = baseCollection.Except(collection1);
Using Except with a given IEqualityComparer (Check also first comment for guidlines):
public class Comparer : IEqualityComparer<YourClass>
{
public bool Equals(YourClass x, YourClass y)
{
// Your implementation
}
public int GetHashCode(YourClass obj)
{
// Your implementation
}
}
var collection1 = baseCollection.Where(r => filterCollection.Contains(r.Property));
var collection2 = baseCollection.Except(collection1, new Comparer());
You can also use GroupBy (probably less good performance wise):
var result baseCollection.GroupBy(r => filterCollection.Contains(r.Property))
.ToDictionary(key => key.Key, value => value.ToList());
var collection1 = result[true];
var collection2 = result[false];
Otherwise another way will just to use a loop:
List<YourType> collection1 = new List<YourType>();
List<YourType> collection2 = new List<YourType>();
foreach(var item in baseCollection)
{
if(filterCollection.Contains(item.Property))
{
collection1.Add(item);
}
else
{
collection2.Add(item);
}
}
In my C# code I have a list List<Tuple<int,string>>. I want to select/convert this to a List<Type>. I want to avoid iterating my list of tuple and insert in other list. Is there any way to do this? Maybe with LINQ?
You cannot change type of list. You only can create new list of another type and fill it with converted values from your list. I suggest to use List<T>.ConvertAll method which exists exactly for this purpose:
List<Tuple<int, string>> tuples = new List<Tuple<int, string>>();
// ...
List<YourType> types =
tuples.ConvertAll(t => new YourType { Foo = t.Item1, Bar = t.Item2 });
You haven't shown this type, but i assume that it contains an int- and a string-property:
List<MyType> result = tupleList
.Select(t => new MyType { IntProperty = t.Item1, StringProperty = t.Item2 })
.ToList();
another option: List.ConvertAll:
List<MyType> result = tupleList.ConvertAll(t => new MyType { IntProperty = t.Item1, StringProperty = t.Item2 });
This presumes that your List<Type> is actually a List<CustomType> (I've called it MyType).
I want to avoid iterating my list of tuple and insert in other list.
LINQ does not avoid loops, it hides them just.
I have 3 .net Lists items, I need to merge them all into one, to order them and bind them to a datagrid. However, I need a way of indicating which original list each item came from, so that I can identify this in the datagrid (change color or font etc).
Can anyone suggest the best way to do this?
List<Foo> list1 = new List<Foo>();
List<Foo> list2 = new List<Foo>();
List<Foo> list3 = new List<Foo>();
var query = list1.Select(foo => new { foo, list = "list1" })
.Concat(list2.Select(foo => new { foo, list = "list2" }))
.Concat(list3.Select(foo => new { foo, list = "list3" }))
.OrderBy(item => item.foo); // whatever you need to order by
Expand the properties as needed.
Assuming that your lists contains items of classes that you can amend I'd suggest that you add a property to those classes that keeps track of which type of the 3 it is. Either as an enum or possibly a reference to the actual list that contained it if you might need to refer back.
If you're not able to do that but assuming that they do contain a name property or similar and it's a readonly grid, a very ugly way would be to add a specific prefix/postfix to the name that says where it came from and then just remove that prefix/postfix before showing it on the screen.
Simple solution, assuming you don't want to modify the original class, or it's a primitive, you can use anonymous types:
var resultList = list1.Select(value => new {value, list = list1})
.Concat(list2.Select(value => new {value, list = list2})
.Concat(list3.Select(value => new {value, list = list3})))
.ToList();
I'd go with something like this:
List<object> list1 = new List<object>();
List<object> list2 = new List<object>();
List<object> list3 = new List<object>();
List<KeyValuePair<int, object>> mergedList = new List<KeyValuePair<int, object>>();
mergedList.AddRange(list1.Select(obj => new KeyValuePair<int, object>(1, obj)));
mergedList.AddRange(list2.Select(obj => new KeyValuePair<int, object>(2, obj)));
mergedList.AddRange(list3.Select(obj => new KeyValuePair<int, object>(3, obj)));
The better solution though, would be to add a property to your object that is some kind of enumeration that tells you something about the object itself. The lists themselves are metadata of some sort - list1 is a list of X, not just a list, so all of it's elements should have some kind of notion of X.
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);
}
});