LINQ fetching sequence values - c#

The below list providing a collection of integers. My requirement is to return sequence values are like first 1,2,3,4,5. once the sequence gets less than or equal 1. fetching will be stopped. I could use for loop to do this operation, but I need to do this using LINQ Extension method. Thanks
If I pass 1, then the result should be like 1,2,3,4,5
If I pass 2, then the result should be like 2,3,4,5
If I pass 3, then the result should be like 3,4,5
List<int> list=new List<int>();
list.Add(1);---------
list.Add(2);---------
list.Add(3);---------
list.Add(4);---------
list.Add(5);---------
list.Add(1);
list.Add(2);
list.Add(3);

var result = list.TakeWhile((item, index) => item > list[0] || index == 0);

You can simply use a loop:
List<int> list2 = new List<int>()
for(int i=0;i<list.Count;i++)
if(i==0 || list[i]>list2[i-1])
list2.Add(list[i]);

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; });

Linq and deferred evaluation

When you use LINQ to define an enumerable collection, either by using the LINQ extension methods or by using query operators,the application
does not actually build the collection at the time that the LINQ
extension method is executed; the collection is enumerated only when
you iterate over it. This means that the data in the original
collection can change between executing a LINQ query and retrieving
the data that the query identifies; you will always fetch the most
up-to-date data.
Microsoft Visual C# 2013 step by step written by John Sharp
I have written the following code:
List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 };
IEnumerable<int> res = numbers.FindAll(a => a > 0).Select(b => b).ToList();
numbers.Add(99);
foreach (int item in res)
Console.Write(item + ", ");
The result of the above code is shown bellow:
1, 2, 3, 4, 5,
Why is that going like this? I know about Func, Action and Predicate but I can not figure out what is going on here. Based on the above definition the code is not rational.
Apart from the ToList() at the end, which is creating a new collection, you have another issue.
The problem is that you are not using LINQ at all.
FindAll is not a LINQ extension method.
You should use Where:
List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 };
IEnumerable<int> res = numbers.Where(a => a > 0);
numbers.Add(99);
foreach (int item in res)
Console.Write(item + ", ");
First you set an int type list that contains 1,2,3,4,5 .
then you used linq to create and define an enumeration collection .
here describes how the linq work :
first find all numbers that they are bigger than zero, as you see all of the items in the above list are bigger than zero , then select all of them and put them in a list . when you add 99 to the number list it doesn't effect on the enumeration collection that defined because it'll create a new collection and pass the items in it and it hasn't any references to the numbers list .
you can delete .ToList() at the end of the linq expression, it'll result in :
1,2,3,4,5,99 .
Good Luck
ToList creates a new instance of List<T> and copy all the items into it:
http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,e276d6892241255b
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
return new List<TSource>(source);
}
So if you want to have 99 in the res you should add it into res, not into numbers:
...
var res = numbers
.Where(a => a > 0) // Filter out; Select is redundant
.ToList();
res.Add(99);
Console.Write(string.Join(", ", res));
The ToList() is not actually the only problem. FindAll returns a new List. So when you call
IEnumerable<int> res = numbers.FindAll(a => a > 0)
That is the same as doing
IEnumerable<int> newList = new List<int>();
foreach (int old in numbers) {
if (old > 0) newList.Add(old);
}
So when you add a new item to numbers, it is no longer relevant. You are searching against the list returned by FindAll rather than the original list.
You will see the result you expect if you defer (or remove all together) the ToList() operation until your foreach loop. ToList will execute the Linq expression the same as enumerating.
List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 };
IEnumerable<int> res = numbers.FindAll(a => a > 0).Select(b => b);
numbers.Add(99);
foreach (int item in res)
Console.Write(item + ", ");
// another option, but not necessary in most cases...
foreach (int item in res.ToList())
Console.Write(item + ", ");

How to filter a list<int> by comparing against a single int variable?

I have a list<int> with values like 10, 20, 30, 56. I also have a local variable int _marks = 30.
How can I print out the values in the list that are less than the value of _marks?
You can use Where() from System.Linq namespace to filter the array. It returns IEnumerable<int> object. And for printing the elements in this collection we can use List<T>.ForEach method. It performs the specified action on each element of the List<T>. And in case of single argument you can pass function by itself:
marks.Where(x => x < _marks).ToList().Foreach(Console.WriteLine);
By the way if you are newbie you can use non-LINQ solution also:
foreach(int item in marks)
{
if(item < _marks)
Console.WriteLine(item);
}
Also, as #Kjartan said, if the list is ordered, then it may be good option to use TakeWhile() or SkipWhile() functions to get desired elements:
// If the list is ordered in ascending order
marks.TakeWhile(x => x < _marks).ToList().Foreach(Console.WriteLine);
// If the list is ordered in descending order
marks.SkipWhile(x => x >= _marks).ToList().Foreach(Console.WriteLine);
You've got several options here. A couple of examples:
var yourList = new List<int>{10, 20, 30, 40, 50, 56, 60, 70};
var _marks = 55;
// Get a IEnumerable containing values matching your condition ( ie. < _marks):
var selected = yourList.Where(i => i < _marks);
Alternative if you know the values are sorted by increasing values (this will avoid iterating through the whole list unnecessarily):
var selected = yourList.TakeWhile(i => i < _marks);
To print, do e.g.:
selected.ToList().ForEach(i => Console.WriteLine(i));

Order a list based on a fixed format

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);

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.

Categories

Resources