custosat
custosat

Reputation: 99

How to parse Text and extract integer

I have this Data.Text

"Check: Find max among: 70, 102, 271, 40"

How do I extract the numbers from this text and apply a function to them?

Upvotes: 3

Views: 1285

Answers (5)

James Brock
James Brock

Reputation: 3426

Here's a one-line solution that works on Text. It pulls numbers out of the Text into a list with the sepCap combinator, and then you could apply the maximum function.

And since this is a parser, you can extend it to something more complicated than this example, like actually parsing the query instructions out of the first part of the string.

import Replace.Megaparsec
import Text.Megaparsec
import Text.Megaparsec.Char.Lexer
import Data.Either
import Data.Maybe
import qualified Data.Text as T

maximum $ rights $ fromJust $ parseMaybe (sepCap (decimal :: Parsec Void T.Text Int)) 
    "Check: Find max among: 70, 102, 271, 40"
271

Upvotes: 0

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477607

For a String you can use:

maximum . map (read) . filter (all isDigit) . map (filter ((/=) ',')) words

Or for a Text, use:

import Prelude as P
import Data.Text as T

fString :: Text -> Integer
fString = maximum . P.map (read) . P.filter (all isDigit) . P.map (P.filter ((/=) ',')) . P.map (T.unpack) . T.words

You can then for instance write a main function to read the value from stdin.

main :: IO ()
main = do
    x <- getLine
    putStrLn $ show $ fString (pack x) --to demonstrate that it works with Data.Text

Upvotes: -2

vikingsteve
vikingsteve

Reputation: 40438

This code example shows a simple way to do it:

import Data.List
import Control.Monad

getLine >>= \line -> putStrLn $ show $ maximum $ (map read . words $ filter (/=',') $ line :: [Int])

Upvotes: 0

ErikR
ErikR

Reputation: 52057

For various esthetic reasons I like this approach:

import qualified Data.Text as T
import Data.Text.Read
import Data.Either
import Data.Char
import Data.Text.IO as T

readNums :: T.Text -> [Int]
readNums =
  map fst .                 -- 5. extract the parsed numbers
  snd .  partitionEithers . -- 4. collect only the valid numbers
  map decimal .             -- 3. parse each substring as an number
  filter (not . T.null) .   -- 2. filter out empty strings (not necessary)
  T.split (not . isDigit)   -- 1. split on non-digits

Upvotes: 2

Thomas M. DuBuisson
Thomas M. DuBuisson

Reputation: 64750

You can use many of the Haskell parsing libraries to parse Text types but for such a simple example I'd just break it into words, filter for the digits and convert. As dfeuer is pointing out, there is a big difference between Haskell's String type which the other answers use and the Text type which you perhaps mistakenly implied. For the text type the operations look quite similar but you don't have the Prelude read operation:

import qualified Data.Text as T
import Data.Text (Text) 
import Data.Char (isDigit) 
import Data.Text.Read 

myRead :: Text -> [Int]
myRead = map num                 -- Convert the remaining elements into Ints
       . filter (not . T.null)   -- Drop all empty words
       . map (T.filter isDigit)  -- Drop all non-digits in each word (including signs!)
       . T.words                 -- Chop the string into words

num :: Text -> Int 
num = either (error . show) fst  -- Throw an exception if it wasn't a signed decimal
    . signed decimal             -- Read a signed decimal

Upvotes: 1

Related Questions