emic
emic

Reputation: 51

The lower value from a tuple of words and values in haskell

I'm trying to write a function that chooses the tuple with the lower value and the tuple is formed by a word and a value.

For example, for a list of tuples like this one: [("CHURCH",262),("PENCIL",311),("FLOUR",175),("FOREST",405),("CLASS",105)], the function would return ("CLASS",105)

Can someone help me out? :)

Thank you so much!

Upvotes: 0

Views: 472

Answers (3)

OderWat
OderWat

Reputation: 5709

I am Haskell beginner but I wanted to find a working solution anyway and came up with:

minTup' :: [(String, Int)] -> Maybe (String, Int) -> (String, Int)
minTup' [] (Just x)= x
minTup' (x:xs) Nothing = minTup' xs (Just x)
minTup' ((s,n):xs) (Just (s',n'))
    | n < n' = minTup' xs (Just (s,n))
    | otherwise = minTup' xs (Just (s',n'))


minTup :: [(String, Int)] -> (String, Int)
minTup [] = error "Need a list of (String,Int)"
minTup x  = minTup' x Nothing

main = do
    print $ minTup [("CHURCH",262),("PENCIL",311),("FLOUR",175),("FOREST",405),("CLASS",105)]
    print $ minTup [("CHURCH",262)]
    print $ minTup []

Upvotes: 1

bheklilr
bheklilr

Reputation: 54058

You're looking for the function minimumBy in the Data.List module. It's type is

minimumBy :: (a -> a -> Ordering) -> [a] -> a

In your case, it would also be useful to import comparing from the Data.Ord module, which has the type

comparing :: Ord a => (b -> a) -> b -> b -> Ordering

And this lets you give it an "accessor" function to create an ordering. So in this case you can do

minimumSnd :: [(String, Int)] -> (String, Int)
minimumSnd = minimumBy (comparing ???)

I'll let you fill in the ???, it should be quite easy at this point. What would your accessor function passed to comparing be for your type?


If you wanted to write a custom function for this instead, I would suggest using the fold pattern. We can also take this opportunity to make it safer by returning a Maybe type, and more general by not requiring it to be a list of (String, Int):

minimumSnd :: (Ord b) => [(a, b)] -> Maybe (a, b)
minimumSnd = go Nothing
    where
        -- If the list is empty, just return our accumulator
        go acc [] = acc
        -- If we haven't found a minimum yet, grab the first element and use that
        go Nothing (x:xs) = go (Just x) xs
        -- If we have a minimum already, compare it to the next element
        go (Just currentMin) (x:xs)
            -- If it's <= our current min, use it
            | snd x <= snd currentMin = go (Just x) xs
            -- otherwise keep our current min and check the rest of the list
            | otherwise = go (Just currentMin) xs

Upvotes: 8

ThreeFx
ThreeFx

Reputation: 7360

Try this:

foldl1 (\acc x -> if snd x < snd acc then x else acc) <your list>

You fold your list by comparing the current element of the list to the next one. If the second value of the next element if smaller than the value of the current element, it is the new value for the accumulator. Else the accumulator stays the same. This process is repeated until the entire list has been traversed.

Upvotes: 1

Related Questions