Ashtoruin
Ashtoruin

Reputation: 25

Haskell list: Replacing elements given their locations in the list

I'm fairly new to Haskell and trying to figure out how I would write a Function to do this and after combing Google for a few hours I'm at a loss on how to do it.

Given the following two lists in Haskell

[(500,False),(400,False),(952,True),(5,False),(42,False)]
[0,2,3]

How would I change the Boolean of the First list at each location given by the second list to a Value of True for an Output of

[(500,True),(400,False),(952,True),(5,True),(42,False)]

Upvotes: 1

Views: 312

Answers (5)

Jeremy List
Jeremy List

Reputation: 1766

Since the approach I would use is not listed:

setTrue spots values = let
  pattern n = replicate n False ++ [True] ++ Repeat False
  toSet = foldl1 (zipWith (||)) $ map pattern spots
  in zipWith (\s (v,o) -> (v, o || s)) toSet values

Upvotes: 0

Markus1189
Markus1189

Reputation: 2869

Or using the fantastic lens library:

setTrue :: [(a,Bool)] -> Int -> [(a,Bool)]
setTrue xs i = xs & ix i . _2 .~ True

setTrues :: [(a,Bool)] -> [Int] -> [(a,Bool)]
setTrues = foldl setTrue

Upvotes: 1

karakfa
karakfa

Reputation: 67467

Straightforward translation from the description will be:

setIndexTrue f a = [(x, p || i `elem` f) | (i, (x,p)) <- zip [0..] a]

Upvotes: 1

asm
asm

Reputation: 8898

This is how I would do it (assumes the list of indexes to replace is sorted).

First we add an index list alongside the list of indexes to replace and the original list. Then we recurse down the list and when we hit the next index to replace we replace the boolean and recurse on the tail of both all three lists. If this is not an index to replace we recurse on the entire replacement index list and the tail of the other two lists.

setTrue :: [Int] -> [(a, Bool)] -> [(a, Bool)]
setTrue is xs = go is xs [0..] -- "Index" the list with a list starting at 0.
  where
    go [] xs _ = xs  -- If we're out of indexes to replace return remaining list.
    go _ [] _ = []   -- If we run out of list return the empty list.
    go indexes@(i:is) (x:xs) (cur:cs)
      | i == cur = (fst x, True) : go is xs cs  -- At the next index to replace.
      | otherwise = x : go indexes xs cs -- Otherwise, keep the current element.

Upvotes: 4

Zeta
Zeta

Reputation: 105876

This is basically the same as Andrew's approach, but it doesn't use an additional index list, and is a little bit more inspired by the traditional map. Note that unlike map, the provided function must be a -> a and cannot be a -> b.

restrictedMap :: (a -> a) -> [Int] -> [a] -> [a]
restrictedMap f is xs = go f is xs 0
  where
    go f [] xs _ = xs
    go f _ [] _ = []
    go f ind@(i:is) (x:xs) n
      | i == n    = f x : go f is xs (n+1)
      | otherwise = x : go f ind xs (n+1)

setTrue = restrictedMap (\(x,_) -> (x, True))

Upvotes: 2

Related Questions