I have a data set something like this:
2,Black
2,Blue
2,Red
1,Small
1,Medium
I need to convert this into the following:
2_0
2_1
2_2
1_0
1_1
The LINQ query I have at the moment uses an index for the second number, however it doesn't reset to 0 when changing from 2_ to 1_. I've tried using a GroupBy, but I can't get the results I need - can anyone help?
IEnumerable<string> output = input
.GroupBy(i => i.Num)
.SelectMany(grp => grp.Select((item, idx) => string.Format("{0}_{1}", grp.Key, idx)));
You can group by the number and use the version of Select() that provides the index:
var result = data.GroupBy(x => x.Number,
(key, g) => g.Select((_, i) => string.Format("{0}_{1}", key, i)))
.SelectMany(x => x);
Note that this might behave differently than you'd expect if the same numbers aren't contiguous: e.g, 2, 2, 2, 1, 1, 2, 2.
Related
I have a requirement to rank the array elements and the array has duplicate values. I tried following this Ranking items in a list with LINQ but this doesn't work when the array has duplicate values in it. Any easy way to do it in c#?
For Example :
input = [650,150,150,200]
output = [1,3,3,2]
For Example :
input = [650,200,200,150]
output = [1,2,2,3]
Update: The requirement is as below, what if I add one more element to the array
Ex: [650,150,150,200,100] output needs to be [1,3,3,2,5] instead of [1,3,3,2,4]
You can create a dictionary as rank-lookup source:
int[] array = new[] {650,150,150,200};
Dictionary<int, int> numRanks = array
.GroupBy(i => i)
.OrderByDescending(g => g.Key)
.Select((g, index) => (num:g.Key, rank:index+1))
.ToDictionary(x => x.num, x => x.rank);
int[] result = array.Select(i => numRanks[i]).ToArray();
For your updated requirement you could use a similar approach using a Lookup<TKey, TValue>:
var rankLookup = array
.OrderByDescending(i => i)
.Select((num, index) => (num, index))
.ToLookup(x => x.num, x => x.index + 1);
int[] result = array.Select(i => rankLookup[i].First()).ToArray();
The lookup is like a dictionary that allows duplicate keys. You need to use First here because you are just interested in the rank. If you'd use Count() you'd know how many duplicates it had.
You could create an array of items, distinct and in order, then use the indices to determine the rank of each item.
var ranks = input.Distinct().OrderByDescending(x => x).ToArray();
var ranked = input.Select(x => Array.IndexOf(ranks, x) + 1);
Working example
Update after comment
If rankings need to be skipped, just remove the Distinct:
var ranks = input.OrderByDescending(x => x).ToArray();
var ranked = input.Select(x => Array.IndexOf(ranks, x) + 1);
Array.IndexOf will take the first element when there are duplicates.
Working example
Let say you have list of items and you want to partition them, make operation on one partition and concatenate partitions back into list.
For example there is list of numbers and I want to partition them by parity, then reverse odds and concatenate with evens. [1,2,3,4,5,6,7,8] -> [7,5,3,1,2,4,6,8]
Sounds simple, but I've got stuck on merging back two groups. How would you do it with LINQ?
IEnumerable<int> result = Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
.??? // need to concatenate
Edit
[1,2,3] is the representation of array which I want to get as the result, not output, sorry if I confused you by that.
The GroupBy method returns an IEnumerable<IGrouping<TKey, TSource>>. As IGrouping implements IEnumerable, you can use SelectMany to concatenate multiple IEnumerable<T> instances into one.
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
.OrderByDescending(p => p.Key)
.SelectMany(p => p);
There are a few ways to achieve this,
so if we start with your function
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
you could then use an Aggregate
.Aggregate((aggrgate,enumerable)=>aggrgate.Concat(enumerable))
this will then go though your list of results and concat them all into a collection and return it, you just need to make sure that aggrgate and enumerable are the same type in this case a IEnumerable<int>
another would be to call SelectMany()
.SelectMany(enumerable=>enumerable)
this then likewise pulls all the enumerables together into a single enumerable, again you need to ensure the types are IEnumerable<int>
other options would be to hard code the keys as Tim is suggesting or pull out of linq and use a loop
You could use this approach using a Lookup<TKey, TElement>:
var evenOddLookup = numbers.ToLookup(i => i % 2);
string result = String.Join(",", evenOddLookup[1].Reverse().Concat(evenOddLookup[0]));
If you don't want a string but an int[] as result:
int[] result = evenOddLookup[1].Reverse().Concat(evenOddLookup[0]).ToArray();
You could do something like this.
var number = string.Join(",",
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2) // Separate even/odd numbers
.OrderByDescending(x=>x.Key) // Sort to bring odd numbers first.
.SelectMany(x=> x.Key ==1? // Sort elements based on even or odd.
x.OrderByDescending(s=>s)
: x.Where(s=> s!=0).OrderBy(s=>s))
.ToArray());
string output = string.Format("[{0}]", number);
Check this Demo
Just use OrderBy like this:
List<int> arr = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
var result = arr.OrderBy(i => i % 2 == 0 ? 1 : 0)
.ThenBy(i => i % 2 == 0 ? i : int.MaxValue)
.ThenByDescending(i => i);
This should give you your desired result as you want:
[1,2,3,4,5,6,7,8] will be converted into [7,5,3,1,2,4,6,8]
I am using linq to sql and my code looks like this:
var agendaLists =
dataContext.view_Agendas.Where(m => m.MeetingID == meetingID)
.OrderBy(n => n.Number)
.ThenBy(n => n.CaseNumber)
.ThenByDescending(s => s.MainYN)
.ToList();
So basicly our "n.Number" is the order number. everything works fine its sorted 1,2,3,4 etc which is correct. but if a object have no value in n.Number it will be displayed in the top but I want it to be placed last.
Today it sorts like this, lets say we get 4 objects back:
Null, 1, 2, 3
I want it sorted like this:
1, 2, 3, Null
Any kind of help is appreciated.
try the following:
n => n.Number ?? int.MaxValue
I dont remember if you actually have to check for DBNull, but it would be the same.
You can use this approach, presuming Number is an int?:
var agendaLists = dataContext.view_Agendas
.Where(m => m.MeetingID == meetingID)
.OrderBy(m => n.Number.HasValue ? 0 : 1)
.ThenBy(n => n.Number)
.ThenBy(n => n.CaseNumber)
.ThenByDescending(s => s.MainYN)
.ToList();
I've a list that contains 4 sized arrays:
These arrays have 4 elements. I want to use another list that contains these arrays' first element's count. In addition, if their first elements are same, they should be summation. For example:
list[0] = {1,2,3,4}
list[1] = {1,1,5,3}
list[2] = {1,2,5,8}
list[3] = {2,2,3,3}
list[4] = {3,5,5,6}
list[5] = {4,4,4,4}
list[6] = {4,5,5,6}
So, anotherList should be:
anotherList = {3, 1, 1, 2}
How can I do this?
EDIT: Expected result is:
anotherList = list.Select(a => a[0]) // project each array to its first item
.GroupBy(x => x) // group first items by their value
.Select(g => g.Count()) // select count of same items
.ToList();
Output:
[ 3, 1, 1, 2 ]
NOTE: GroupBy internally uses Lookup which returns groups in same order as the are added, so it seems to be what you want.
UPDATE: Approach which does not depend on internal implementation of GroupBy
anotherList = list.Select((a,i) => new { Item = a[0], Index = i })
.GroupBy(x => x.Item)
.OrderBy(g => g.Min(x => x.Index))
.Select(g => g.Count())
.ToList();
What would be the most efficient path to the result I'm looking for? I'm using ASP.NET 4.5 and C#.
Let say I have the following int list:
5,7,2,7,8,5,8,0,2,9,8,8,7
I want to remove duplicates from the list and order it by the number of appearances of each item. I know I can use LINQ's Distinct() to make the items unique, but how can I also make it order by the number of appearances? This is the desired result for the example above:
8,7,5,2,0,9
You could group the items and then order them by the count in each group:
var items = new[] { 5, 7, 2, 7, 8, 5, 8, 0, 2, 9, 8, 8, 7 };
var result = items
.GroupBy(x => x)
.OrderByDescending(x => x.Count())
.Select(x => x.Key);
var ordered = list
.GroupBy(x => x)
.OrderByDescending(g => g.Count())
.Select(g => g.Key);