Sorting an ArrayList - c#

Based on my previous question, I've trying now to have them in the following order using the same approach, OrderByDescending and ThenBy
Original (can be in any random order):
1:1
0:0
0:1
2:1
1:0
2:0
Output
2:0
1:0
0:0
2:1
1:1
0:1
as you can see, a is descending, and b being ascending. But I'm still not getting the right sort. Any ideas why? Thanks

Think to what you would do manually:
First you must sort the values by the 2nd part in ascending order
Then you must sort values having the same 2nd part, using the 1st part in descending order
Translated in LINQ it's pretty the same:
var sorted = arrayList
.Cast<string>()
.Select(x => x.Split(':'))
.OrderBy(x => x[1])
.ThenByDescending(x => x[0])
.Select(x => x[0] + ":" + x[1]);
To clarify a bit more, ThenBy/ThenByDescending methods are used to sort elements that are equal in the previous OrderBy/OrderByDescending, hence the code :)

arrayList.ToList().Select(i => { var split = i.Split(":".ToArray(),2));
return new { a = Int32.Parse(split[0]),
b = Int32.Parse(split[1}) };
})
.OrderByDescending(i => i.a)
.ThenBy(i => i.b)
From your question it is not clear whether you want the order-by's reversed (just swap them).
Work from there, perhaps rejoining
.Select(i => String.Format("{0}:{1}", i.a, i.b));
Good luck

Related

Sorting an array by another array's values using OrderByDescending() does not sort the array

I'm trying to implement a simple Huffman coding algorithm. I take my input string (ddddbbcccaeeeee) and use it to create 2 arrays, those being a char array called usedCharacters and an int array called characterCounts. However these arrays need to be sorted by the number of times the character appears in the input string so the Huffman tree can be constructed. I tried using LINQ's OrderByDescending() method like I had seen online:
usedCharacters = usedCharacters.OrderByDescending(i => characterCounts).ToArray();
characterCounts = characterCounts.OrderByDescending(i => i).ToArray();
The program runs but when I check the results the characters are very obviously still in order as they appear in the input string, meaning no sorting is actually done. On the other hand, characterCounts does succesfully sort. I also tried the more commonly seen online solution of usedCharacters.OrderByDescending(i => characterCounts.IndexOf(i)).ToArray() but that just causes an index out of bounds exception for reasons I don't fully understand. If anybody could give me some insight into what I'm missing that would be greatly appreciated. Thank you!
The simplest way to achieve what you're trying to do is to use a GroupBy expression.
var s = "ddddbbcccaeeeee";
var list = s.GroupBy(x => x)
.Select(x => new { Char = x.Key, Count = x.Count() })
.OrderByDescending(x => x.Count);
foreach(var item in list)
{
Console.WriteLine(item.Char + " " + item.Count);
}
The code treats s as a character array and counts instances of all characters. The OrderByDescending then sorts by Count.
The output of code below should look something like this:
e 5
d 4
c 3
b 2
a 1
Your LINQ statement is trying to sort each element in usedCharacters by an by a constant int[]. It doesn't do anything like matching up the elements of both arrays. It's exactly as if you are doing this:
usedCharacters = usedCharacters.OrderByDescending(i => 42).ToArray();
It just leaves the array in the same order.
If you have two separate list and you want to order the first based on the second then you need to use Zip like this:
usedCharacters =
usedCharacters
.Zip(characterCounts)
.OrderByDescending(x => x.Second)
.Select(x => x.First)
.ToArray();
If you have the initial string of characters then this is the simplest way to get your result:
string characters = "ddddbbcccaeeeee";
char[] usedCharacters =
characters
.GroupBy(x => x)
.OrderByDescending(x => x.Count())
.Select(x => x.Key)
.ToArray();

C# change from groupby

Suppose there are two properties in Myclass: Date, Symbol
I want to frequently convert between those two properties, but I find that
for List <Myclass> vector
if I use
vector.groupby(o => o.Date).Select(o => o)
the vector is no longer the type of List<IGrouping<string, Myclass>>
And if I want to convert groupby(o => o.Date) to groupby(o => o.Symbol)
I have to use
vector.groupby(o => o.Date).Selectmany(o => o).groupby(o => o.Symbol)
I try to use SortedList<Date, Myclass>, but I am not familiar with SortedList(actually, I don't know what's the difference between SortedList and Groupby).
Is there any effective way to achieve such effect, as I highly depend on the speed of running?
int volDay = 100;
Datetime today = new DateTime(2012, 1, 1);
//choose the effective database used today, that is the symbol with data more than volDay
var todayData = dataBase.Where(o => o.Date <= today).OrderByDescending(o => o.Date)
.GroupBy(o => o.Symbol).Select(o => o.Take(volDay))
.Where(o => o.Count() == volDay).SelectMany(o => o);
//Select symbols we want today
var symbolList = todayData
.Where(o => o.Date == today && o.Eqy_Dvd_Yld_12M > 0))
.OrderByDescending(o => o.CUR_MKT_CAP)
.Take((int)(1.5 * volDay)).Where(o => o.Close > o.DMA10)
.OrderBy(o => o.AnnualizedVolatility10)
.Take(volDay).Select(o => o.Symbol).ToList();
//Select the database again only for the symbols in symbolList
var portfolios = todayData.GroupBy(o => o.Symbol)
.Where(o=>symbolList.Contains(o.Key)).ToList();
This is my real code, dataBase is the total data, and I will run the cycle day by day(here just given a fixed day). The last List portfolios is the final goal I want obtain, you can ignore other properties, which are used for the selections under the collection of Date and Symbol
It may be faster, or at least easier to read, if you performed a .Distinct().
To get distinct Dates:
var distinctDates = vector.Select(o => o.Date).Distinct()
To get distinct Symbols:
var distinctSymbols = vector.Select(o => o.Symbol).Distinct()
I asked what you were trying to accomplish so that I can provide you with a useful answer. Do you need both values together? E.g., the unique set of symbols and dates? You should only need a single group by statement depending on what you are ultimately trying to achieve.
E.g., this question Group By Multiple Columns would be relevant if you want to group by multiple properties and track the two unique pieces of data. a .Distinct() after the grouping should still work.

Split list into duplicate and non-duplicate lists

So far I have this:
List<Item> duplicates = items.GroupBy(x => x.Id)
.SelectMany(g => g.Skip(1)).ToList();
List<Item> nonDuplicates = items.GroupBy(x => x.Id)
.Select(x => x.First()).ToList();
Is there a more efficient way to do this (i.e. one select)?
Example input:
Id Value (added for some perspective)
-- -----
1 12
1 909
1231 0
1 577
Example Output:
duplicates -> {1, 909}, {1, 577}
non-duplicates -> {1, 12}, {1231, 0}
If you really want to avoid doing the actual grouping more than once, and thus avoid iterating the source sequence more than once, you can group the items, materialize that query into a list, and then grab the info that you want from that list.
var query = items.GroupBy(x => x.id)
.ToList();
var duplicates = query.SelectMany(group => group.Skip(1));
var nonDuplicates = query.Select(group => group.First());
Having said that, grouping items isn't particularly expensive of an operation, so this may not actually be a particularly huge win. Odds are reasonably high that your existing code is "good enough".
I'd be mostly interested in doing this if I wasn't confident that the source sequence would return the same items if iterated multiple times, or if it's say an IQueryable that needs to do a round trip to the database to get the items. In those cases this is a change worth implementing.
Get the first one for each Id, then use Except to get the others.
List<Item> nonDupes = items.GroupBy(x => x.Id).Select(x => x.First()).ToList();
List<Item> dupes = items.Except(nonDupes).ToList();
This is, however, assuming that Equals hasn't been overridden to be simply the Id.
EDIT: And here's a fiddle: http://dotnetfiddle.net/4GaPK4
var result = items.GroupBy(x => x.Id)
.Select(g => new {
Dups = g.Where(g.Count > 1),
NonDups = g.Where(g.Count == 1), })
.ToList();
Not in one query, but arguably more efficient approach can be achieved by using DistinctBy from MoreLinq:
var nonDuplicates = items.DistinctBy(i => i.Id);
var duplicates = items.Except(nonDuplicates);

Rx how to group by a key a complex object and later do SelectMany without "stopping" the stream?

This is related to my other question here. James World presented a solution as follows:
// idStream is an IObservable<int> of the input stream of IDs
// alarmInterval is a Func<int, TimeSpan> that gets the interval given the ID
var idAlarmStream = idStream
.GroupByUntil(key => key, grp => grp.Throttle(alarmInterval(grp.Key)))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key)));
<edit 2:
Question: How do I start the timers immediately without waiting for the first events to arrive? That's the root problem in my question, I guess. For that end, I planned on sending off dummy objects with the IDs I know should be there. But as I write in following, I ended up with some other problems. Nevertheless, I'd think solving that too would be interesting.
Forwards with the other interesting parts then! Now, if I'd like to group a complex object like the following and group by the key as follows (won't compile)
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key)))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key)));
then I get into trouble. I'm unable to modify the part about SelectMany, Concat and Observable.Return so that the query would work as before. For instance, if I make query as
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key)))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key.First())))
.Subscribe(i => Console.WriteLine(i.Id + "-" + i.IsTest);
Then two events are needed before an output can be observed in the Subscribe. It's the effect of the call to First, I gather. Furthermore, I woul like to use the complex object attributes in the call to alarmInterval too.
Can someone offer an explanation what's going on, perhaps even a solution? The problem in going with unmodified solution is that the grouping doesn't look Ids alone for the key value, but also the IsTest field.
<edit: As a note, the problem probably could be solved firsly by creating an explicit class or struct and then that implements a custom IEquatable and secondly then using James' code as-is so that grouping would happen by IDs alone. It feels like hack though.
Also, if you want to count the number of times you've seen an item before the alarm goes off you can do it like this, taking advantage of the counter overload in Select.
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key))
.SelectMany(grp => grp.Select((count, alarm) => new { count, alarm }).TakeLast(1));
Note, this will be 0 for the first (seed) item - which is probably what you want anyway.
You are creating an anonymous type in your Select. Lets call it A1. I will assume your idStream is an IObservable. Since this is the Key in the GroupByUntil you do not need to worry about key comparison - int equality is fine.
The GroupByUntil is an IObservable<IGroupedObservable<int, A1>>.
The SelectMany as written is trying to be an IObservable<A1>. You need to just Concat(Observable.Return(grp.Key)) here - but the the type of the Key and the type of the Group elements must match or the SelectMany won't work. So the key would have to be an A1 too. Anonymous types use structural equality and the return type would be stream of A1 - but you can't declare that as a public return type.
If you just want the Id, you should add a .Select(x => x.Id) after the Throttle:
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key)
.Select(x => x.Id))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key)));
If you want A1 instead - you'll need to create a concrete type that implements Equality.
EDIT
I've not tested it, but you could also flatten it more simply like this, I think this is easier! It is outputing A1 though, so you'll have to deal with that if you need to return the stream somewhere.
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key))
.SelectMany(grp => grp.TakeLast(1));

GroupBy and OrderBy

I'm trying to do a GroupBy and then OrderBy to a list I have. Here is my code so far:
reportList.GroupBy(x => x.Type).ToDictionary(y=>y.Key, z=>z.OrderBy(a=>a.Lost));
With the help of the last question I asked on linq I think the ToDictionary is probably unneeded, but without it I don't know how to access the inner value.
To be clear, I need to GroupBy the Type property and want the inner groups I get to be OrderBy the Lost property (an integer). I want to know if there is a better, more efficient way or at the least better then what I've done.
An explanation and not just an answer would be very much appreciated.
Yes, there is better approach. Do not use random names (x,y,z,a) for variables:
reportList.GroupBy(r => r.Type)
.ToDictionary(g => g.Key, g => g.OrderBy(r => r.Lost));
You can even use long names to make code more descriptive (depends on context in which you are creating query)
reportList.GroupBy(report => report.Type)
.ToDictionary(group => group.Key,
group => group.OrderBy(report => report.Lost));
Your code does basically the following things:
Group elements by type
Convert the GroupBy result into a dictionary where the values of the dictionary are IEnumerables coming from a call to OrderBy
As far as the code correctness it is perfectly fine IMO, but maybe can be improved in term of efficiency (even if depends on your needs).
In fact, with your code, the values of your dictionary are lazily evaluated each time you enumerate them, resulting in a call to OrderBy method.
Probably you could perform it once and store the result in this way:
var dict = reportList
.GroupBy(x => x.Type)
.ToDictionary(y => y.Key, z => z.OrderBy(a => a.Lost).ToList());
// note the ToList call
or in this way:
var dict = reportList.OrderBy(a => a.Lost)
.GroupBy(x => x.Type)
.ToDictionary(y => y.Key, z => z);
// here we order then we group,
// since GroupBy guarantees to preserve the original order
Looks fine to me. If you use an anonymous type instead of a Dictionary, you could probably improve the readability of the code that uses the results of this query.
reportList.GroupBy(r => r.Type)
.Select(g => new { Type = g.Key, Reports = g.OrderBy(r => r.Lost) });

Categories