F# Seq.sortBy in descending order
I am fairly new to F# and came by the Seq.sortBy function however it is sorting my list in ascending order. How do I get it to sort in descending order using the Seq.sort?
For instance an example code would be...
let DisplayList =
seq{0..10}
|> S开发者_开发问答eq.sortBy(fun x -> x)
|> Seq.iter(fun x -> Console.WriteLine(x.ToString()))
gives me an output of 1 2 3 4 5 6 7 8 9 10, when I really want it to do it from 10 to 1.
F# 4.0 (Visual Studio 2015) introduced Seq.sortByDescending
and Seq.sortDescending
let DisplayList =
seq { 0..10 }
|> Seq.sortDescending ' or |> Seq.sortByDescending id
|> Seq.iter Console.WriteLine
See https://github.com/Microsoft/visualfsharp/wiki/F%23-4.0-Status and https://github.com/fsharp/FSharpLangDesign/blob/master/FSharp-4.0/ListSeqArrayAdditions.md
Even shorter:
seq { 0..10 }
|> Seq.sortBy (~-) // Unary minus
|> Seq.iter (printfn "%d")
Looking at the other answers, beware unary minus and MININT:
let a = [| 1; -1; System.Int32.MinValue; 0; System.Int32.MaxValue; 1 |]
printfn "%A" (a |> Array.sortBy (fun x -> x))
// [|-2147483648; -1; 0; 1; 1; 2147483647|]
printfn "%A" (a |> Array.sortBy (fun x -> -x)) // uh-oh!
// [|-2147483648; 2147483647; 1; 1; 0; -1|]
I think you actually want negative-x-minus-one:
printfn "%A" (a |> Array.sortBy (fun x -> -x - 1))
// [|2147483647; 1; 1; 0; -1; -2147483648|]
for a wraparound integer type that spans -2^N..2^N-1
.
First, let's extend Seq
with a sortWith
function same as List and Array have.
namespace Microsoft.FSharp.Collections
module Seq =
let sortWith f e =
let e' = e |> Seq.toArray
e' |> Array.sortInPlaceWith f
e' |> Seq.readonly
Next, let's extend Operators
with an often useful flip
function.
namespace Microsoft.FSharp.Core
module Operators =
let flip f x y = f y x
Now, we can leverage the generic compare
function for generic (you can use this with any sequence of comparable elements) and safe (in regard to Brian's observation) reverse sequence sort.
{0..10}
|> Seq.sortWith (flip compare)
|> Seq.iter (printfn "%A")
Another option is to wrap System.Linq.Enumerable.OrderByDescending()
:
// #r "System.Core"
module Seq =
let sortByDesc f s = Enumerable.OrderByDescending(s, new Func<'a, 'b>(f))
{0..10} |> Seq.sortByDesc (fun x -> x)
You can fix this by providing a negative key
let DisplayList =
seq { 0..10 }
|> Seq.sortBy (fun x -> -x)
|> Seq.iter (fun x -> Console.WriteLine(x.ToString()))
Also it's a bit easier (and type safer) to use the printf
functions for displaying text in F#. For example
let DisplayList =
seq { 0..10 }
|> Seq.sortBy (fun x -> -x)
|> Seq.iter (printfn "%d")
If you know, ahead of time, that you'll have a relatively small sequence, I think this is more readable...
let x = seq { 0.. 10 } |> Seq.toArray |> Array.rev
Of course, its not advisable if you got a potentially very large sequence.
Solutions that use unary minus: (fun x -> -x - 1)
and (fun x -> -x)
don't work when you have unsigned types:
let a = [| 0uy; 255uy; 254uy; 1uy |]
printfn "%A" (a |> Array.sortBy (fun x -> -x - 1))
// error FS0001: The type 'byte' does not support the operator '~-'
Instead we can use the fact that -x = ~~~x + 1
where ~~~
is a bitwise negation operator and thus -x - 1 = ~~~x
. So the short solution that works for both signed and unsigned types:
Array.sortBy (~~~) // equivalent to Array.sortBy (fun x -> ~~~x)
Examples:
let a = [| 0uy; 255uy; 254uy; 1uy |]
printfn "%A" (a |> Array.sortBy (~~~))
// [|255uy; 254uy; 1uy; 0uy|]
let a = [| 1; -1; System.Int32.MinValue; 0; System.Int32.MaxValue; 1 |]
printfn "%A" (a |> Array.sortBy (~~~))
// [|2147483647; 1; 1; 0; -1; -2147483648|]
精彩评论