Почему мой код разбирает отсортированный файл JSON в несортированный список?|Хаскель и Эсон - PullRequest
0 голосов
/ 17 октября 2018

Я пытаюсь проанализировать вложенный файл JSON, который выглядит следующим образом

{
    "Meta Data": {
        "1: Symbol": "MSFT",
        "2: Indicator": "Relative Strength Index (RSI)",
        "3: Last Refreshed": "2018-10-17 10:31:05",
        "4: Interval": "weekly",
        "5: Time Period": 10,
        "6: Series Type": "open",
        "7: Time Zone": "US/Eastern Time"
    },
    "Technical Analysis: RSI": {
        "2018-10-17 10:31:05": {
            "RSI": "54.5653"
        },
        "2018-10-12": {
            "RSI": "63.0279"
        },
        "2018-10-05": {
            "RSI": "74.7519"
        },
        "2018-09-28": {
            "RSI": "72.1573"
        },
        "2018-09-21": {
            "RSI": "74.8123"
        },
        "2018-09-14": {
            "RSI": "66.7116"
        },
        "2018-09-07": {
            "RSI": "75.8051"
          ...
          ...
          ... and so on

Моя цель - получить самое последнее / самое верхнее значение "RSI".Поскольку ключ (даты) являются переменными, я пытаюсь получить его, не сопоставляя ключи.


Это то, что я получил до сих пор

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

module Tesst where

import           Data.Aeson
import qualified Data.ByteString.Lazy as B
import           Data.HashMap.Strict  as S
import           Data.Text
import           Data.Time
import           GHC.Exts
import           GHC.Generics
import           Network.HTTP.Conduit (simpleHttp)

newtype TechnicalAnalysis = TechnicalAnalysis {
                   rsiQuote  :: Object
                   } deriving (Show, Generic)


instance FromJSON TechnicalAnalysis where
  parseJSON = withObject "RSI Quote" $
    \o -> do
      rsiQuote <- o        .: "Technical Analysis: RSI"
      return TechnicalAnalysis {..}

main :: IO ()
main = do
  putStrLn "Which ticker? "
  symbolToQuote <- getLine
  d <- (eitherDecode <$> simpleHttp ("https://www.alphavantage.co/query?function=RSI&interval=weekly&time_period=10&series_type=open&apikey=FI2KMDSAE&symbol=" ++ symbolToQuote)) :: IO (Either String TechnicalAnalysis)
  case d of
    Left e   -> print e
    Right qt -> print $ Prelude.map (\(a, b) -> (read (unpack a) :: Day, b)) (S.toList (rsiQuote qt))

Я запускаю код так:

[(2005-09-02,Object (fromList [("RSI",String "60.8860")])),(2017-12-29,Object (fromList [("RSI",String "71.1921")])),(2011-09-16,Object (fromList [("RSI",String "50.3489")])),(2000-05-12,Object (fromList [("RSI",String "33.6402")]))...

Как видите, все значения не в порядке.


Мои вопросы:

  1. Почему бы просто не распечатать этот файл JSON как есть?
  2. Можно ли получить последнее значение RSI без сортировки результата?

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Вы выбрали динамически типизированный Object в качестве своего семантического объекта.Выбери что-нибудь получше;Map Day Double, например, или ArgMax Day Double, если вы действительно хотите только последнее значение RSI и никогда не будете смотреть на что-либо еще.

Например:

import Control.Monad
import Data.Aeson
import Data.Aeson.Types
import Data.HashMap.Strict as S
import Data.List.NonEmpty
import Data.Semigroup
import Data.Text
import Data.Time

-- can't use readMaybe, 2018-10-17 10:31:05 has extra non-Day gunk at the end
jsonReadText :: Read a => String -> Text -> Parser a
jsonReadText err t = case reads (unpack t) of
    (v,_):_ -> return v
    _ -> fail $ "could not parse " ++ unpack t ++ " as " ++ err

newtype RSI = RSI Double deriving Show

instance FromJSON RSI where
    parseJSON = withObject "object with RSI key" $ \o -> do
        t <- o .: pack "RSI"
        RSI <$> jsonReadText "Double" t

newtype LatestRSI = LatestRSI (ArgMax Day RSI) deriving Show

instance FromJSON LatestRSI where
    parseJSON = withObject "RSI Quote" $ \o -> do
        quoteMap <- o .: pack "Technical Analysis: RSI"
        quotes <- forM (S.toList quoteMap) $ \(dayText, rsiValue) -> do
            day <- jsonReadText "Day" dayText
            rsi <- parseJSON rsiValue
            return (Max (Arg day rsi))
        case quotes of
            [] -> fail "expected non-empty collection of RSI quotes"
            q:qs -> return . LatestRSI . sconcat $ q :| qs

Пример использования в ghci:

> bs <- Data.ByteString.Lazy.readFile "test.json"
> eitherDecode bs :: Either String LatestRSI
Right (LatestRSI (Max {getMax = Arg 2018-10-17 (RSI 54.5653)}))
0 голосов
/ 17 октября 2018

Мне удалось решить проблему с указаниями Виллема (в комментариях).

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

module Tesst where

import           Data.Aeson
import qualified Data.ByteString.Lazy as B
import           Data.HashMap.Strict  as S
import           Data.List
import           Data.Text
import           Data.Time
import           GHC.Exts
import           GHC.Generics
import           Network.HTTP.Conduit (simpleHttp)


newtype TechnicalAnalysis = TechnicalAnalysis {
                   rsiQuote  :: Object
                   } deriving (Show, Generic)


instance FromJSON TechnicalAnalysis where
  parseJSON = withObject "RSI Quote" $
    \o -> do
      rsiQuote <- o        .: "Technical Analysis: RSI"
      return TechnicalAnalysis {..}

main :: IO ()
main = do
  putStrLn "Which ticker? "
  symbolToQuote <- getLine
  d <- (eitherDecode <$> simpleHttp ("https://www.alphavantage.co/query?function=RSI&interval=weekly&time_period=10&series_type=open&apikey=FI2KMC2DSAE&symbol=" ++ symbolToQuote)) :: IO (Either String TechnicalAnalysis)
  case d of
    Left e   -> print e
    Right qt -> do
      let l = S.toList (rsiQuote qt)
      print $ maximumBy (\(a, _) (b, _) -> compare a b) l

Это был ключ

maximumBy (\(a, _) (b, _) -> compare a b)
...