Get the first few elements from List on C# - c#

I have List with n items. I wish transform my list to new list, in which no more than n first items.
Example for n=3:
[1, 2, 3, 4, 5] => [1, 2, 3]
[1, 2] => [1, 2]
What is the shortest way to do this?

If you have C# 3, use the Take extension method:
var list = new [] {1, 2, 3, 4, 5};
var shortened = list.Take(3);
See: http://msdn.microsoft.com/en-us/library/bb503062.aspx
If you have C# 2, you could write the equivalent:
static IEnumerable<T> Take<T>(IEnumerable<T> source, int limit)
{
foreach (T item in source)
{
if (limit-- <= 0)
yield break;
yield return item;
}
}
The only difference is that it isn't an extension method:
var shortened = SomeClass.Take(list, 3);

If you don't have LINQ, try:
public List<int> GetFirstNElements(List<int> list, int n)
{
n = Math.Min(n, list.Count);
return list.GetRange(0, n);
}
otherwise use Take.

You can use Take
myList.Take(3);

You can with Linq
List<int> myList = new List<int>();
myList.Add(1);
myList.Add(2);
myList.Add(3);
myList.Add(4);
myList.Add(5);
myList.Add(6);
List<int> subList = myList.Take<int>(3).ToList<int>();

var newList = List.Take(3);

Related

Operate LINQ queries on 'vertical' rather than 'horizontal' space

I can't figure out how to do this, if even possible.
An example:
int[][] myArrays = {
new int[] {1, 2, 3},
new int[] {4, 5, 3},
new int[] {1, 2, 3}
};
int[] avgArray = myArrays.//Some LINQ statement to average every Nth element in the second dimension (essentially "flatten" the array)
//avgArray == {2, 3, 3}
To do this so far, I can only think of:
int ndDimLen = myArrays.GetLength(1);
int[] avgArray = new int[ndDimLen];
myArrays.Select(
arr => arr.Select( (n, i) => avgArray[i] += n )
);
avgArray = avgArray.Select( n => n / ndDimLen ).ToArray();
But this defeats the purpose, and isn't a particularly good idea on jagged arrays...
Also, I'd definitely like to avoid transposition, as it's quite a slow operation when operating on large arrays!
Thank you for your time!
You could iterate throught the [Columns] index while a [Row].Length reports that it contains a [Column] in the dimension whose values you need to average.
(Using the terms Column and Row for simplicity, as a visual aid)
An example, using Linq's .Average() to compute the average value of the sequence:
int[][] myArrays = {
new int[] {1, 2, 3},
new int[] {4, 5, 3},
new int[] {1, 2, 3},
};
int column = 2;
double avg = myArrays.Select((arr, i) => myArrays[i][column]).Average();
Result: 3
With a more complex structure, we need to verify whether the current [Column] contains a value:
int[][] myArrays = {
new int[] {1, 2, 3},
new int[] {3, 4, 5},
new int[] {3, 4},
new int[] {1, 2, 3, 4},
new int[] {1},
new int[] {4, 5, 3}
};
int column= 2;
double? avg = myArrays.Select((arr, i) =>
((arr.Length > column) ? myArrays?[i][column] : null)).Average();
Result Column 1: { 1, 3, 3, 1, 1, 4 } => 2.1666666...
Result Column 2: { 2, 4, 4, 2, 5 } => 3.4
Result Column 3: { 3, 5, 3, 3 } => 3.5
Result Column 4: { 4 } => 4
Same method, applied to all the [Columns]:
var Averages = Enumerable.Range(0, myArrays.Max(Arr => Arr.Length))
.Select(col => myArrays
.Select((arr, idx) =>
((arr.Length > col) ? myArrays?[idx][col] : null))
.Average()).ToList();
Enumerable.Range gives some flexibility.
The code above generates a series of int elements starting from 0 and incrementing the value to the number of Colums in the Array (Max(Arr => Arr.Length) selects the Array's Row containing the higher number of elements).
So, you could average the numbers in the first Column only (Index = 0) with:
var Averages = Enumerable.Range(0, 1).Select( ... )
or from Columns 1 to 3 (Indexes 1 to 3):
var Averages = Enumerable.Range(1, 3).Select( ... )
Yes, it is possible, but not on this object.
Basically, myArrays is an array of arrays, so LINQ only sees one row at a time, you cannot make it to see columns, because it's just not how it works.
What you could do, is to transpose this "table" first, that is change places of columns and rows. How to do it has already been discussed here so I will just refer you to it.
Using knowledge how to transpose it, you can make a method that will do it, and use LINQ on it, like:
Transpose(myArray).Select(predicate);
You didn't specify what you want if the arrays have unequal length:
int[][] myArrays =
{
new int[] {1, 2},
new int[] {4, 5, 3, 7, 5, 3, 4, 5, 1},
new int[] {1, 2, 3}
};
Let's assume your arrays all have the same length.
If you plan to use this functionality regularly, consider writing an extension function for two dimensional arrays. See Extension Methods Demystified
public static IEnumerable<int> ToVerticalAverage(this int[][] array2D)
{
if (array2D == null) throw new ArgumentNullException(nameof(array2D);
// if input empty: return empty
if (array2D.Any())
{ // there is at least one row
// check number of columns:
int nrOfColumns = array2D.First().Length;
if (!array2D.All(row => row.Length == nrOfColumns)
{
// TODO: decide what to do if arrays have unequal length
}
// if here, at least one row, all rows have same number of columns
for(int columNr = 0; columNr < nrOfColumns; ++columNr)
{
yield return array2D.Select(row => row[i]).Average();
}
}
// else: no rows, returns empty sequence
}
Usage:
int[][] myInputValues = ...
IEnumerable<int> averageColumnValues = myInputValues.ToVerticalAverage();
If you have several functions where you need the values of the columns, write an extension function to fetch the columns (= transpose the matrix)
public static IEnumerable<IEnumerable<int>> Transpose(this int[][] array2D)
{
// TODO: check input
int nrOfColumns = array2D.First().Length;
for(int columNr = 0; columNr < nrOfColumns; ++columNr)
{
yield return array2D.Select(row => row[columnNr];
}
}
public static IEnumerable<int> ToVerticalAverage(this int[][] array2D)
{
// TODO: check input
foreach (IEnumerable<int> column in array2D.Transpose())
{
yield return column.Average();
}

How to get first index of Binary Search's results?

I have some problems. I have 2 list such as:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5};
List<int> secondList = new List<int> { 2, 3, 1 };
⇒ True result is: {1, 3, 0}
I would like to get the first index of numbers in secondList that exists in firstList. I used list.BinarySearch() but the result was {2, 3, 0}.
List<int> firstList = new List<int> { 1, 2, 2, 3, 5};
List<int> secondList = new List<int> { 2, 3, 1 };
var output = secondList.Select(item => firstList.IndexOf(item)); // [1 , 3 , 0]
You can replace the IndexOf with a BinarySearch logic, but BinarySearch returns the first matched element index, so you won't get the lowest number, IndexOf does return the lowest matching index.
The problem is that when the list contains duplicate values as in your case, the BinarySearch method will return the index of any of the matching values (non deterministic).
To get the desired result, you could create and use a custom extension method like this:
public static class ListExtensions
{
public static int BinarySearchFirst<T>(this List<T> source, T item, IComparer<T> comparer = null)
{
if (comparer == null) comparer = Comparer<T>.Default;
int index = source.BinarySearch(item, comparer);
while (index > 0 && comparer.Compare(source[index], source[index - 1]) == 0)
index--;
return index;
}
}
Sample usage:
var result = secondList.Select(x => firstList.BinarySearchFirst(x)).ToList();
// { 1, 3, 0 }
C++ has a standard library function for this called lower_bound().
Here's a C# implementation. This is useful if you are searching large collections:
public static int LowerBound<T>(IList<T> values, T target, int first, int last)
where T : IComparable<T>
{
int left = first;
int right = last;
while (left < right)
{
int mid = left + (right - left) / 2;
var middle = values[mid];
if (middle.CompareTo(target) < 0)
left = mid + 1;
else
right = mid;
}
return left;
}
That doesn't return -1 for elements that it doesn't find, so to fix that we can wrap it like so:
public static int LowerBoundOrMinusOne<T>(IList<T> values, T target, int first, int last)
where T : IComparable<T>
{
int result = LowerBound(values, target, first, last);
if (result >= last || result < first || values[result].CompareTo(target) != 0)
return -1;
return result;
}
Here is how you use it:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };
List<int> secondList = new List<int> { 2, 3, 1 };
List<int> result = secondList
.Select(value => LowerBoundOrMinusOne(firstList, value, 0, firstList.Count))
.ToList();
Console.WriteLine(string.Join(", ", result));
Of course, this is mainly of benefit to large lists because it has an O(Log2(N)) rather than an O(N) complexity.
Iterate through second array and get index of element in first array:
foreach (int item in secondList)
{
Console.WriteLine(firstList.IndexOf(item));
}
If you have a large firstList and so you have to use BinarySearch try amending it: find out the item (which is not guaranteed to be the leftmost one) by BinarySearch, then move to the left while having read the same item:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };
List<int> secondList = new List<int> { 2, 3, 1, 123 };
var result = secondList
.Select(item => firstList.BinarySearch(item))
.Select(index => index < 0 ? -1 : Enumerable
.Range(0, index + 1) // we have to scan [0..index] at the worst case
.Select(i => index - i) // scan in reverse
.TakeWhile(i => firstList[index] == firstList[i]) // take while items are the same
.Last()); // finally, we want the last item
Test
// 1, 3, 0, -1
Console.Write(String.Join(", ", result));

Generate random number from two List<>

i want to generate random number from two list. i want to create a function where i pass how much random number from two list.
List<int> integers = new List<int>() { 54, 23, 76, 123, 93, 7, 3489 };
List<int> value2 = new List<int>() { 1, 3, 4, 6, 8, 17, 40 };
i want my result = List<int> result = {54,40,123,17,3,1,3489,76...etc}
When i run again the set of result will be change.
Presently i am using this function that return List
public static List<int> GenerateRandom(int count)
{
// generate count random values.
HashSet<int> candidates = new HashSet<int>();
while (candidates.Count < count)
{
// May strike a duplicate.
candidates.Add(random.Next(1,30));
}
// load them in to a list.
List<int> result = new List<int>();
result.AddRange(candidates);
// shuffle the results:
int i = result.Count;
while (i > 1)
{
i--;
int k = random.Next(i + 1);
int value = result[k];
result[k] = result[i];
result[i] = value;
}
return result;
}
i am calling the function
List<int> vals = GenerateRandom(20);
But i want that the random number from two List<> List<int> integers and List<int> value2 . so how can i do this .
You can do something like this:
var result =
integers.Concat(value2)
.OrderBy(x => random.Next())
.Take(count)
.ToList();
You could write a general-purpose function to give you a random ordering of any number of sequences, like so:
public static IReadOnlyCollection<T> InRandomOrder<T>(Random rng, params IEnumerable<T>[] lists)
{
return lists
.SelectMany(x => x)
.OrderBy(y => rng.Next())
.ToList();
}
You can then pass as many lists as you like and get the contents back in a fully randomised order:
var list1 = new[] {1, 2, 3, 4, 5};
var list2 = new[] {6, 7, 8};
var list3 = new[] {9, 0};
Random rng = new Random();
for (int i = 0; i < 10; ++i)
{
var randomisedFirst5 = InRandomOrder(rng, list1, list2, list3).Take(5);
Console.WriteLine(string.Join(", ", randomisedFirst5));
}
There's a less efficient approach you can use that avoids the need for an instance of Random, but you should only use this for short lists or where you really don't care about performance. It uses Guid.NewGuid() to generate random numbers:
public static IReadOnlyCollection<T> InRandomOrder<T>(params IEnumerable<T>[] lists)
{
return lists
.SelectMany(x => x)
.OrderBy(y => Guid.NewGuid())
.ToList();
}
Even the more efficient approach isn't the fastest. A faster way would be to use reservoir sampling to take the first N items that you want, and put them into an array which you shuffle using Knuth. That would make it a lot faster, at the expense of more complicated code - meaning you should only do it the fast way if it's really needed.
If what you want is to select a number that exists either in list A or B, randomly, you can do:
List<int> integers = new List<int>() { 54, 23, 76, 123, 93, 7, 3489 };
List<int> value2 = new List<int>() { 1, 3, 4, 6, 8, 17, 40 };
List<int> allInOne = new List<int>(integers.Concat(value2));
Random r = new Random(DateTime.Now.Millisecond);
/********************************
For demonstration purposes
********************************/
for(int i = 0; i < 5; i++)
{
var randomListIndex = r.Next(0, allInOne.Count - 1);
Console.WriteLine(allInOne[randomListIndex]);
}
Use KeyValuePair
static void Main(string[] args)
{
List<KeyValuePair<int, int>> results = GenerateRandom(100);
}
static List<int> integers = new List<int>() { 54, 23, 76, 123, 93, 7, 3489 };
static List<int> value2 = new List<int>() { 1, 3, 4, 6, 8, 17, 40 };
static Random random = new Random();
public static List<KeyValuePair<int,int>> GenerateRandom(int count)
{
List<KeyValuePair<int,int>> result = new List<KeyValuePair<int,int>>();
for(int i = 0; i < count; i++)
{
int firstValue = integers[random.Next(0, integers.Count - 1)];
int seconValue = value2[random.Next(0, value2.Count - 1)];
result.Add(new KeyValuePair<int,int>(firstValue,seconValue));
}
return result;
}​
I actually made a library a while back that handles some of this stuff : Underscore.cs
It's a nuget package so easy to install, the code to shuffle or take a sample randomly of two lists is :
var ls1 = GenerateRandom(10);
var ls2 = GenerateRandom(20);
var mixer = ls1.Concat(ls2).ToList();
//if you want all of the items shuffled use shuffle
var result = _.List.Shuffle(mixer);
//or if you want a subset randomly sorted use sample
result = _.List.Sample(mixer);

Linq fill function

Is there a Linq operator that will ensure that a collection is a minimum size
What I would like to see is:
int[] x = {1, 2, 3, 4};
var y = x.Fill(6);
// y is now {1, 2, 3, 4, 0, 0}
Note (from the answers so far) I'm looking for something that will work with IEnumerable<T>. int[] was just for easy initialization in the example
No, but an extension method wouldn't be hard:
public static IEnumerable<T> PadRight<T>(this IEnumerable<T> source, int length)
{
int i = 0;
// use "Take" in case "length" is smaller than the source's length.
foreach(var item in source.Take(length))
{
yield return item;
i++;
}
for( ; i < length; i++)
yield return default(T);
}
Usage:
int[] x = {1, 2, 3, 4};
var y = x.PadRight(6);
// y is now {1, 2, 3, 4, 0, 0}
y = x.PadRight(3);
// y is now {1, 2, 3}
No, but you can combine Concat, Repeat and Take to achieve this.
public static IEnumerable<T> PadRight<T>(this IEnumerable<T> source, int length) =>
source.Concat(Enumerable.Repeat(default(T), length)).Take(length);
There's nothing in Linq that would do that in a single call - also note that because linq is for querying not modifying, any such method wouldn't change your collection anyway (but might return an extended version of your collection).
That said, an extension method like this would return such a result:
public static IEnumerable<T> Fill<T>(this IEnumerable<T> target, int minLength)
{
int i = 0;
foreach (var item in target)
{
i++;
yield return item;
}
while (i < minLength)
{
i++;
yield return default(T);
}
}
int minLength = 6;
var x = new[] { 1, 2, 3, 4 };
x.Select(i => { minLength--; return i; }).Concat(
Enumerable.Repeat(0, minLength).TakeWhile(i => minLength-- > 0));

Removing sequential repeating items from List<T> using linq

I'm looking for a way to prevent repeating items in a list but still preserve the order.
For example
1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4
should become
1, 2, 3, 4, 1, 2, 3, 4
I've done it quite inelegantly using a for loop, checking the next item as follows
public static List<T> RemoveSequencialRepeats<T>(List<T> input)
{
var result = new List<T>();
for (int index = 0; index < input.Count; index++)
{
if (index == input.Count - 1)
{
result.Add(input[index]);
}
else if (!input[index].Equals(input[index + 1]))
{
result.Add(input[index]);
}
}
return result;
}
Is there a more elegant way to do this, preferably with LINQ?
You can create extension method:
public static IEnumerable<T> RemoveSequentialRepeats<T>(
this IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
var comparer = EqualityComparer<T>.Default;
if (!iterator.MoveNext())
yield break;
var current = iterator.Current;
yield return current;
while (iterator.MoveNext())
{
if (comparer.Equals(iterator.Current, current))
continue;
current = iterator.Current;
yield return current;
}
}
}
Usage:
var result = items.RemoveSequentialRepeats().ToList();
You can also use pure LINQ:
List<int> list = new List<int>{1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4};
var result = list.Where((x, i) => i == 0 || x != list[i - 1]);
If you really really hate the world, pure LINQ:
var nmbs = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4, 5 };
var res = nmbs
.Take(1)
.Concat(
nmbs.Skip(1)
.Zip(nmbs, (p, q) => new { prev = q, curr = p })
.Where(p => p.prev != p.curr)
.Select(p => p.curr));
But note that you'll need to enumerate (at least partially) the enumerable 3 times (the Take, the "left" part of Zip, the first parameters of Zip). This method is slower than building a yield method or doing it directly.
Explanation:
You take the first number (.Take(1))
You take all the numbers from the second (.Skip(1)) and pair it with all the numbers (.Zip(nmbs). We will call curr the numbers from the first "collection" and prev the numbers from the second "collection" ((p, q) => new { prev = q, curr = p })). You then take only the numbers that are different from the previous number (.Where(p => p.prev != p.curr)) and from these you take the curr value and discard the prev value (.Select(p => p.curr))
You concat these two collections (.Concat()
you could write simple LINQ :
var l = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 };
var k = new Nullable<int>();
var nl = l.Where(x => { var res = x != k; k = x; return res; }).ToArray();
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
or pythonic (well, my best try) way:
l.Zip(l.Skip(1), (x, y) => new[] { x, y })
.Where(z => z[0] != z[1]).Select(a => a[0])
.Concat(new[] { l[l.Length - 1] }).ToArray()
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
the simplest one (edit: haven't seen that it already suggested by King King)
l.Where((x, i) => i == l.Length - 1 || x != l[i + 1]).ToArray()
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
If you want LINQ statement that do not rely on captured value of result inside the call you'll need some construct with aggregate as it is the only method that carries value along with operation. I.e. based on Zaheer Ahmed's code:
array.Aggregate(new List<string>(),
(items, element) =>
{
if (items.Count == 0 || items.Last() != element)
{
items.Add(element);
}
return items;
});
Or you can even try to build list without if:
array.Aggregate(Enumerable.Empty<string>(),
(items, element) => items.Concat(
Enumerable.Repeat(element,
items.Count() == 0 || items.Last() != element ? 1:0 ))
);
Note to get reasonable performance of above samples with Aggregate you'd need to also carry last value (Last will have to iterate whole sequence on each step), but code that carries 3 values {IsEmpty, LastValue, Sequence} in a Tuple is very strange looking. These samples are here for entertaining purposes only.
One more option is to Zip array with itself shifted by 1 and return elements that are not equal...
More practical option is to build iterator that filters values:
IEnumerable<string> NonRepeated(IEnumerable<string> values)
{
string last = null;
bool lastSet = false;
foreach(var element in values)
{
if (!lastSet || last != element)
{
yield return element;
}
last = element;
lastSet = true;
}
}
check if last of new list and current item is not same then add to new list:
List<string> results = new List<string>();
results.Add(array.First());
foreach (var element in array)
{
if(results[results.Length - 1] != element)
results.Add(element);
}
or using LINQ:
List<int> arr=new List<int>(){1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 };
List<int> result = new List<int>() { arr.First() };
arr.Select(x =>
{
if (result[result.Length - 1] != x) result.Add(x);
return x;
}).ToList();
Do have proper validation for null object.
Try this:
class Program
{
static void Main(string[] args)
{
var input = "1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 ";
var list = input.Split(',').Select(i => i.Trim());
var result = list
.Select((s, i) =>
(s != list.Skip(i + 1).FirstOrDefault()) ? s : null)
.Where(s => s != null)
.ToList();
}
}
Here the code you need :
public static List<int> RemoveSequencialRepeats(List<int> input)
{
var result = new List<int>();
result.Add(input.First());
result.AddRange(input.Where(p_element => result.Last() != p_element);
return result;
}
The LINQ magic is:
result.Add(input.First());
result.AddRange(input.Where(p_element => result.Last() != p_element);
Or you can create extension method like this:
public static class Program
{
static void Main(string[] args)
{
List<int> numList=new List<int>(){1,2,2,2,4,5,3,2};
numList = numList.RemoveSequentialRepeats();
}
public static List<T> RemoveSequentialRepeats<T>(this List<T> p_input)
{
var result = new List<T> { p_input.First() };
result.AddRange(p_input.Where(p_element => !result.Last().Equals(p_element)));
return result;
}
}
If you feel like referencing an F# project you can write
let rec dedupe = function
| x::y::rest when x = y -> x::dedupe rest
| x::rest -> x::dedupe rest
| _ -> []

Categories

Resources