Order a list based on a fixed format - c#

int[] OrderedListToFollow = {1,2,4,5}
int[] ListB = {2,3,4,8,9}
Based on the two lists above I need to sort ListB based on the order defined in OrderedListToFollow. Since 3,8,9 are not part of the OrderedListToFollow those can appear in any order, so the acceptable solutions could have any of the following :
int[] ListB = {2,4,3,8,9}
int[] ListB = {2,4,8,3,9}
int[] ListB = {2,4,9,3,8}
I tried doing this as follows but it does not order it :
ListB = ListB.OrderBy(id => OrderedListToFollow.ToList().IndexOf(id)).ToArray();
EDIT
The order above works but it places the items not present in OrderedListToFollow first and then the remaining.

The problem with your sorting method is that the IndexOf method returns -1 if the item is not found. Thus, all your items which exist "outside" the given ordering scheme are placed at the beginning of the collection because they get an index of -1.
You could try using a conditional to return the index if found and the current index otherwise:
var c = ListB.Count();
ListB = ListB
.OrderBy(id => OrderedListToFollow.Contains(id)
? OrderedListToFollow.ToList().IndexOf(id)
: c + 1 // This will always pace invalid objects at the end
);

As noted, it was already working apart from putting the results at the beginning. To fix this, I'd add an extension method:
public static int IndexOfOrMax(this IEnumerable<T> source, T item)
{
int index = source.IndexOf(item);
return index == -1 ? int.MaxValue : index;
}
Also note that you don't need to call ToList on OrderedListToFollow - currently you're calling that a lot, which is very inefficient. With the above extension method in place, you can use:
int[] orderedListToFollow = {1,2,4,5};
int[] listB = {2,3,4,8,9};
listB = listB.OrderBy(id => orderedListToFollow.IndexOfOrMax(id)).ToArray();

How about something like this:
int[] OrderedListToFollow = {1,2,4,5};
int[] ListB = {2,3,4,8,9};
List<int> ListC = new List<int>();
ListC.AddRange(OrderedListToFollow.Where(p => ListB.Contains(p)));
ListC.AddRange(ListB.Where(p => !OrderedListToFollow.Contains(p)));
This will give you a result like this :
2
4
3
8
9

You can do this using Join and Except, the benefit being that they're both ~O(n) operations (due to both using Hashtables in their implementations). This snippet hinges on the assumption that OrderedListToFollow is indeed ordered.
int[] OrderedListToFollow = new[]{1,2,4,5};
int[] ListB = new[]{3,4,8,2,9};
var existing = from o in OrderedListToFollow
join l in ListB on o equals l
select l;
var other = ListB.Except(OrderedListToFollow);
var result = existing.Concat(other);

Related

How do i convert List<int[]> into List<int>

private static void A(){
List<int[]> list = new List<int[]>();
int[] a = {0,1,2,3,4};
int[] b = {5,6,7,8,9};
list.Add(a);
list.Add(b);
List<int> list2 = new List<int>();
// list2 should contain {0,1,2,3,4,5,6,7,8,9}
}
How would i convert my List<int[]> list into a List<int> so that all the int[] arrays become one giant list of numbers
It looks like you might be looking for LINQ .SelectMany method.
A quote from MSDN:
Projects each element of a sequence to an IEnumerable and flattens
the resulting sequences into one sequence
List<int> list2 = list.SelectMany(l => l).ToList();
If you want numbers to be ordered in a specific order you could use .OrderBy before executing the query (which will be executed when we call .ToList)
List<int> list2 = list.SelectMany(l => l).OrderBy(i => i).ToList();
What about?
var list2 = a.Concat(b).ToList();
I would use the aggregate function. It is better when you have bigger collection.
List<int> list2 = list.Aggregate(new List<int>(), (ag, l) => { ag.AddRange(l); return ag; });

C# find a number common to two or more lists [duplicate]

This question already has answers here:
Intersection of multiple lists with IEnumerable.Intersect()
(8 answers)
Closed 7 years ago.
What is the syntax for finding a list of numbers both present in two or more List? I was looping through when I only needed to check two lists, but now I need to do several...
Something like
List<int> commonIds = SELECT id from list1
where list1.contains(id),
list2.contains(id),
list3.contains(id) ...
Finding the intersection of any number of lists
If you want to find the set of numbers that exist in all of the lists then Enumerable.Intersect is a good way to do so. You don't even have to hardcode the collection of lists, it can be created at runtime:
var lists = new[] { list1, list2, ..., listN }; // dynamically specified
var common = lists.First().AsEnumerable();
foreach (var list in lists.Skip(1))
{
common = common.Intersect(list);
}
// and now common has the result, e.g.
var listOfCommonEntries = common.ToList();
Finding the union of intersections between master list and each other one
If you want to find the set which includes all common numbers between list 1 and list 2, union all common numbers between list 1 and list N, then it's somewhat different:
var common = Enumerable.Empty<int>();
foreach (var list in lists.Skip(1))
{
common = common.Union(lists.First().Intersect(list));
}
You can use Enumerable.Intersect:
List<int> commonIds = list1.Intersect(list2.Intersect(list3)).ToList();
It's pretty efficient since it's using a set.
Here is a function,
it returns the union of the intersect of all 2 set combinations.
public static IEnumerable<T> SharedItems<T>(this IEnumerable<IEnumerable<T>> source)
{
var pairs =
from s1 in source
from s2 in source
select new { s1 , s2 };
var intersects = pairs
.Where(p => p.s1 != p.s2)
.Select(p => p.s1.Intersect(p.s2));
return intersects.SelectMany(i => i).Distinct();
}
to customize
void Main()
{
List<int> one = new List<int>() {1, 3, 4, 6, 7};
List<int> second = new List<int>() {1, 2, 4, 5};
foreach(int r in one.Intersect(second))
Console.WriteLine(r);
}

Get identical elements in two lists with LINQ [duplicate]

I have two lists:
List<int> data1 = new List<int> {1,2,3,4,5};
List<string> data2 = new List<string>{"6","3"};
I want do to something like
var newData = data1.intersect(data2, lambda expression);
The lambda expression should return true if data1[index].ToString() == data2[index]
You need to first transform data1, in your case by calling ToString() on each element.
Use this if you want to return strings.
List<int> data1 = new List<int> {1,2,3,4,5};
List<string> data2 = new List<string>{"6","3"};
var newData = data1.Select(i => i.ToString()).Intersect(data2);
Use this if you want to return integers.
List<int> data1 = new List<int> {1,2,3,4,5};
List<string> data2 = new List<string>{"6","3"};
var newData = data1.Intersect(data2.Select(s => int.Parse(s));
Note that this will throw an exception if not all strings are numbers. So you could do the following first to check:
int temp;
if(data2.All(s => int.TryParse(s, out temp)))
{
// All data2 strings are int's
}
If you have objects, not structs (or strings), then you'll have to intersect their keys first, and then select objects by those keys:
var ids = list1.Select(x => x.Id).Intersect(list2.Select(x => x.Id));
var result = list1.Where(x => ids.Contains(x.Id));
From performance point of view if two lists contain number of elements that differ significantly, you can try such approach (using conditional operator ?:):
1.First you need to declare a converter:
Converter<string, int> del = delegate(string s) { return Int32.Parse(s); };
2.Then you use a conditional operator:
var r = data1.Count > data2.Count ?
data2.ConvertAll<int>(del).Intersect(data1) :
data1.Select(v => v.ToString()).Intersect(data2).ToList<string>().ConvertAll<int>(del);
You convert elements of shorter list to match the type of longer list. Imagine an execution speed if your first set contains 1000 elements and second only 10 (or opposite as it doesn't matter) ;-)
As you want to have a result as List, in a last line you convert the result (only result) back to int.
public static List<T> ListCompare<T>(List<T> List1 , List<T> List2 , string key )
{
return List1.Select(t => t.GetType().GetProperty(key).GetValue(t))
.Intersect(List2.Select(t => t.GetType().GetProperty(key).GetValue(t))).ToList();
}

Sort List<T> with an array

Basically I have a list containing all items. Then I have a string containing the IDs I want to grab out from the list, to do this I split the string into an Int array and later use this and LINQ to get the items I want from the list
Like this :
List<T> lstAllList = Model.getAllItems();
string stringIDs = "8,9,12,11,7";
int[] intArray = stringIDs.Split(',').Select(n => Convert.ToInt32(n)).ToArray();
List<T> lstLimitedList = (from r in lstAllList where intArray.Contains(r.id) select r).ToList();
Which works great.
But the issue here is that I want to have my list ordered in the same way as the string of IDs is, i.e 8,9,12,11,7 like in this example.
But the returned list from the LINQ sorts it by the id by default, so the returned list is 7,8,9,11,12.
Is there a way to prevent it from sorting it like this or is there a way to sort the new list with my int array?
Sure, just sort by the index of the ID in the array:
string stringIDs = "8,9,12,11,7";
int[] intArray = stringIDs.Split(',').Select(n => Convert.ToInt32(n)).ToArray();
var lstLimitedList = (
from r in lstAllList
where intArray.Contains(r.id)
orderby Array.IndexOf(intArray, r.id) // <--------
select r).ToList();
Simply getting the elements one at a time may be faster than trying to resort. (Anyone willing to calculate the O() costs?)
List<T> lstLimitedList = new List<T>();
foreach(int id in intArray)
{
lstLimitedList.Add(lstAllList.Where(item => item.id = id));
}
You could also use intArray.ForEach() if you're a LINQ maniac, but this is much easier to read. ;)
Try to rotate your query. Under word rotate I mean start with intArray and use join. Something like this:
List<T> lstLimitedList = (
from id in intArray
join item in lstAllList on id equals item.Id
select item).ToList();
Id use the intersect extension method with provided by LINQ!
int[] array ={ 8,9,12,11,7} // or your result from your split on string;
List<int> array2 = new List<int> { 8,9,12,11,7 } // your model.GetAllItems;
// Call Intersect extension method.
var intersect = array.Intersect(array2);
// Write intersection to screen.
foreach (int value in intersect)
{
Console.WriteLine(value); // Output: 8,9,12,11,7
}
Bit cleaner for me
Stop overusing LINQ guys. In this case linq is a total overkill. A much simpler and better performance-wise solution is the following:
string a = "8,9,12,11,7";
List<int> list = new List<int>();
string[] splitted = a.Split(',');
for (int i = 0; i < splitted.Length; i++)
{
list.Add(int.Parse(splitted[i]));
}
Which a single loop and without sorting etc.

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