-1

I have the following list of Value that I'd like to sort by the delta key.

{-# LANGUAGE Haskell2010 #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson

lists :: [Value]
lists =
    [ object ["label" .= String "foo", "delta" .= Number 2]
    , object ["label" .= String "bar", "delta" .= Number 3]
    , object ["label" .= String "baz", "delta" .= Number 1]
    ]
amitaibu
  • 1,026
  • 1
  • 7
  • 23

1 Answers1

1

This is quite a bit more involved thanks to the structure of Aeson. I'll post the code without much explanation, other than "sortBy and comparing are your friends":

import Data.Aeson.Types (parseMaybe, Value, Parser)
import Data.Ord (comparing)
import Data.List (sortBy)
import Data.Scientific (Scientific)
import Data.Text (Text)

sortByKeyForNumberField :: Text -> [Value] -> [Value]
sortByKeyForNumberField key = sortBy (comparing $ parseMaybe parserFunction)
  where
    parserFunction :: Value -> Parser Scientific
    parserFunction = withObject "some object" (.: key)

You can then calculate your desired list by evaluating sortByKeyForNumberField "delta" lists.

However, you're much better off parsing Aeson's AST to a meaningful data type (which you'll have to define yourself, because you know the application) and implementing sorting for it. Aeson is really made as an AST for parsing and rendering JSON, and not for manipulating JSON.

  • Thank you. Can you give a pointer on what you mean by "parsing Aeson's AST to a meaningful data type". – amitaibu Oct 28 '16 at 07:07
  • 1
    Define a data type that suits your application (what does the object with a 'label' and a 'delta' *mean*?), implement functions to process it (e.g. an accessor function for the 'delta', so that you can use it with `sortBy`) and implement a parser for it (usually by creating an `instance FromJSON YourDataType`). –  Oct 28 '16 at 07:25
  • Oh, I see - makes sense. Indeed in my real-world case (i.e. not the simplified version in the question) my list of `Value` comes from properly defined types. It is done by `mappend`ing a list of `entityIdToJSON` persistent's `Entity`. So I think I must use code like you posted in the answer, or is there a better way? - https://gist.github.com/amitaibu/6b43645692addceebff2548cf2add0b7#file-example-hs-L13 – amitaibu Oct 28 '16 at 07:41
  • If you have an Entity type and a function entityFoo :: Entity -> Foo then as long as Foo is an instance of Ord you can sort a [Entity] with "sortBy (comparing entityFoo)" – Paul Johnson Oct 28 '16 at 11:39
  • Or, what's probably more appropriate: only use `Foo`-typed objects after you retrieve the data from storage and parsed it. Your list would be `[Foo]`, and not `[Entity]` or `[Value]`, and then you can simply `sortBy (comparing getDeltaFromFoo)` (only implement an `Ord` instance if it makes sense to talk about the "order" of `Foo` objects without further context). –  Oct 28 '16 at 12:24