How to group the same values in a sequence with LINQ? - c#

I have a sequence. For example:
new [] { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 }
Now I have to remove duplicated values without changing the overall order. For the sequence above:
new [] { 10, 1, 5, 25, 45, 40, 100, 1, 2, 3 }
How to do this with LINQ?

var list = new List<int> { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
List<int> result = list.Where((x, index) =>
{
return index == 0 || x != list.ElementAt(index - 1) ? true : false;
}).ToList();
This returns what you want. Hope it helped.

var list = new List<int> { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
var result = list.Where((item, index) => index == 0 || list[index - 1] != item);

It may be technically possible (though I don't think you can with a one-liner) to solve this with LINQ, but I think it's more elegant to write it yourself.
public static class ExtensionMethods
{
public static IEnumerable<T> PackGroups<T>(this IEnumerable<T> e)
{
T lastItem = default(T);
bool first = true;
foreach(T item in e)
{
if (!first && EqualityComparer<T>.Default.Equals(item, lastItem))
continue;
first = false;
yield return item;
lastItem = item;
}
}
}
You can use it like this:
int[] packed = myArray.PackGroups().ToArray();
It's unclear from the question what should be returned in the case of 1,1,2,3,3,1. Most answers given return 1,2,3, whereas mine returns 1,2,3,1.

You can use Contains and preserve order
List<int> newList = new List<int>();
foreach (int n in numbers)
if (newList.Count == 0 || newList.Last() != n)
newList.Add(n);
var newArray = newList.ToArray();
OUTPUT:
10, 1, 5, 25, 45, 40, 100, 1, 2, 3

Did you try Distinct?
var list = new [] { 10, 20, 20, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
list = list.Distinct();
Edit: Since you apparently only want to group items with the same values when consecutive, you could use the following:
var list = new[] { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
List<int> result = new List<int>();
foreach (int item in list)
if (result.Any() == false || result.Last() != item)
result.Add(item);

Related

Get array items by index

I have two arrays, one with values an one with indices
int[] items = { 1, 2, 3, 7, 8, 9, 13, 16, 19, 23, 25, 26, 29, 31, 35, 36, 39, 45 };
int[] indices = { 1, 3, 5, 6, 7, 9 };
now I want a result array from the items selected by the indices of indices array
// 2, 7, 9, 13, 19
int[] result = new []{ items[1], items[3], items[5], items[6], items[7], items[9] };
Question: Is there a more generic approach for this?
var results = Array.ConvertAll(indices, i => items[i]);
Try using Linq:
int[] items = { 1, 2, 3, 7, 8, 9, 13, 16, 19, 23, 25, 26, 29, 31, 35, 36, 39, 45 };
int[] indices = { 1, 3, 5, 6, 7, 9 };
int[] result = indices
.Select(index => items[index])
.ToArray();
A good old for loop should be able to do this job as well:
int[] items = { 1, 2, 3, 7, 8, 9, 13, 16, 19, 23, 25, 26, 29, 31, 35, 36, 39, 45 };
int[] indices = { 1, 3, 5, 6, 7, 9 };
List<int> resultList = new List<int>();
for (int i = 0; i < indices.Length; i++)
{
resultList .Add(items[indices[i]]);
}
Explanation:
when using the [ ] operator to access a specific index in indices it will return the number. This can again be used to index/access a specific location in items. So you have a double indexing.
EDIT:
If you need the result as an array you can use the ToArray method to convert it:
int [] result = resultList.ToArray();
For the sake of alternative:
int[] result = items.Select((value, index) => new { Index = index, Value = value }) //Add indexes
.Where(w => indices.Contains(w.Index)) //Filter by indexes
.Select(s => s.Value).ToArray(); //Extract values to result array

Build pairs out of list - without repetition

to do it in linqI have an integer List and want to group these to a list of integer pairs.
var input = new[] {1, 24, 3, 2, 26, 11, 18, 13};
result should be: {{1, 24}, {3, 2}, {26, 11}, {18, 13}}
I tried:
List<int> src = new List<int> { 1, 24, 3, 2, 26, 11, 18, 13 };
var agr = src.Select((n, i) => new Tuple<int, int>(i++ % 2, n))
.GroupBy(t => t.Item1)
.ToList();
var wanted = agr[0].Zip(agr[1], (d, s) => new Tuple<int, int>(d.Item2, s.Item2));
Is there a better way to do it in linq?
Of course I can do it with a simple for-loop.
Edit:
I think I give MoreLinq a try. I also mark this as the answer even if it's an extension and not pure linq.
By the way - I think doing it with a for-loop is much more understandable.
You can use MoreLINQ Batch to split your input into a list of "length 2" lists. Or any other length you want.
List<int> src = new List<int> { 1, 24, 3, 2, 26, 11, 18, 13 };
List<IEnumerable<int>> wanted = src.Batch(2).ToList();
No need for MoreLINQ; Enumerate even- and odd-indexed values, and Zip
int[] input = new int[8] { 1, 24, 3, 2, 26, 11, 18, 13 };
var evenPositioned = input.Where((o, i) => i % 2 == 0);
var oddPositioned = input.Where((o, i) => i % 2 != 0);
var wanted = evenPositioned.Zip(oddPositioned, (even, odd) => new { even, odd }).ToList();
If you can garantee, that the length of the source can always be devided by 2:
List<int> src = new List<int> { 1, 24, 3, 2, 26, 11, 18, 13 };
var Tuple<int, int>[] wanted = new Tuple<int, int>[src.Count /2];
for(var i = 0; i < src.Count; i = i + 2)
wanted[i/2] = new Tuple<int, int>(src[i], src[i+1]);
a simple for loop is enough for this.Just start with 1 and increment it by 2
List<int> src = new List<int> { 1, 24, 3, 2, 26, 11, 18, 13 };
var list = new List<Tuple<int, int>>();
for(int i =1;i<src.Count;i=i+2)
{
list.Add(new Tuple<int, int>(src[i-1],src[i]));
}
In case of odd count last item will be skipped
Another simple loop solution featuring C# 7 tuples.
var input = new List<int> { 1, 24, 3, 2, 26, 11, 18, 13 };
var wanted = new List<(int, int)>();
for (var index = 0; index < input.Count; index += 2) {
wanted.Add((input[index], input[index + 1]));
}

Arrange Array in C# Issue

I've an array sequence 20,40,60,10,30,50. I want to sort this sequence into the following order 60,40,50,20,30,10 in C#.
Any Help? Thanks in advance☺
Very Simple if you have an Array
int[] arr = { 1, 2, 3, 5, 9, 0, 2, 10 };
arr.OrderBy(a => a);
arr.Reverse();
in case of list
List<int> abc = new List<int> { 1, 2, 3, 5, 9, 0, 2, 10 };
abc.Sort();
abc.Reverse();
Just use OrderByDescending of LINQ:
var list = new[] {20, 40, 60, 10, 30, 50};
var newList = list.OrderByDescending(x => x);
Console.WriteLine(string.Join(",", newList)); //60,50,40,30,20,10
You can try this
int[] array = new int[] { 20, 40, 60, 10, 30, 50 };
Array.Sort<int>(array,
new Comparison<int>((element1, element2) => element1.CompareTo(element2)));
to reverse sort
element2.CompareTo(element1)

Get indices of top x equal elements

I need to get the indices of all the largest elements who are equal to each other in a sorted list of ints.
So given this list
elements: {1 , 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 11, 12, 13, 13, 13}
index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
^ ^ ^
I will get this output
{16,17,18}
So far I've got
list.Select((x, i) => new {x, i})
To get the indices, but I can't use OrderBy() with First() or Single() because I need all the max elements' indices, not just the very top one.
Is there an elegant way to accomplish this (with LINQ or otherwise)?
Thus items are sorted, you only need to get index of first item with max value (that item will have exactly same value as last item), and then create range of indexes starting from this index to the end of list:
var items = new List<int> {1,1,2,3,4,4,5,6,7,7,8,9,10,11,11,12,13,13,13};
int startIndex = items.IndexOf(items[items.Count - 1]);
var indexes = Enumerable.Range(startIndex, items.Count - startIndex);
The easy/lazy way:
var a = new[] {1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 11, 12, 13, 13, 13};
var b = new List<int>();
var max = a.Max();
for (var i = 0; i < a.Length; i++)
{
if (a[i] == max) b.Add(i);
}
I wouldn't use LINQ since it is a simple foreach over the collection.
//var data = new[] {1, 1, 13, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 11, 12, 13, 13, 13};
var data = new[] {1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 11, 12, 13, 13, 13};
var largest = int.MinValue;
var indices = new List<int>();
foreach (var x in data.Select((value, idx) => new {value, idx}))
{
if (x.value > largest)
{
indices.Clear();
largest = x.value;
}
// if unsorted
//if (x.value == largest) indices.Add(x.idx);
// if sorted you don't need to check against largest
indices.Add(x.idx);
}
Console.WriteLine("largest = {0}; indices = {1}", largest, string.Join(", ", indices));
Though if you must use LINQ, you can use this option instead of the foreach:
data.Select((value, idx) => new {value, idx})
.Aggregate(indices, (idxs, n) =>
{
if (n.value > largest)
{
idxs.Clear();
largest = n.value;
}
//unsorted
if (n.value == largest) idxs.Add(n.idx);
//sorted
//idxs.Add(n.idx);
return idxs;
});
This will get you a result of all the elements that have duplicates with their indices:
var result = elements.Select((value, index) => new { value, index })
.Where(g => elements.FindAll(v => v == g.value).Count > 1)
.GroupBy((a) => a.value).OrderByDescending((g) => g.Key).Take(3);
//I placed Take(3) as example since you said you need to find
//elements who are equal to each other,so only those that are
// not distinct(have duplicates) get into the collection.
//this will loop through the results and display the value(the item
//on the list) and its respective index.
foreach (var item in result.SelectMany(g => g))
{
string outcome = item.value + " - " + item.index;
Console.WriteLine(outcome);
}

Filter C# collection of float given a minimum difference value between adjacent elements

Suppose I have an ordered list of float (ascending).
I want to remove from it each element whose difference between next element and itself is less than a given threshold.
I need something like this:
List<float> orderedList;
IEnumerable<float> query = orderedList.Where(currentNum , nextNum => nextNum - currentNum < threshold);
Is that possible? If yes how?
Try this out:
var filteredElements = new List<float>();
float ? prev = null;
orderedList.ToList().ForEach((e)=>{
if (prev.HasValue)
{
if (e-prev >= threshold)
filteredElements.Add(prev.Value);
}
prev = e
});
Try this -
List<float> orderedList = new List<float>() { 12, 14, 34, 45 };
List<float> itemsToRemove = orderedList.Where((item, index) =>
index < orderedList.Count - 1 &&
orderedList[index + 1] - item < threshhold).ToList();
Try this instead :
var ls1 = ls.Where((item, index) =>
item - ls[ls.Count == index + 1 ? index : index + 1] >= 0);
Hope this will help !!
This appears to work. (Though there are some potentially misunderstood implications of your question.)
var input = new List<float>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 18, 21, 24, 27, 29, 35, 40, 46, 59 };
var output = input.Zip(input.Skip(1).Concat(new[]{float.MaxValue}), (a, b) => new { a, b }).Where(x => x.b - x.a > 2).Select(x => x.a);
This produces the output of:
15, 18, 21, 24, 29, 35, 40, 46, 59
This has the advantage of working with any IEnumerable.

Categories

Resources