0

I am looking for a function that replaces a character at a given index.

For example:

replace 4 '!' "Hello World!"
-- Output: "Hell! World!"
3
  • What should happen if the index is less than 0, or greater than the length of the string? Commented Jan 9, 2021 at 22:18
  • Good point! I think such checking would be best implemented with pattern matching. Maybe the original string could be returned. Commented Jan 9, 2021 at 22:20
  • 1
    One liner: replace i r s = [if j == i then r else c | (j, c) <- zip [0..] s]. May not be the most efficient, but for short strings, it shouldn't really matter. Commented Jan 9, 2021 at 23:35

3 Answers 3

1

You can improve it slighly by using replacement : strAfter, and using a safe tail function to prevent an error if the index is greater than the length of the string:

safeTail :: [a] -> [a]
safeTail [] = []
safeTail (_:xs) = xs

replaceCharAtIndex :: Int -> Char -> String -> String
replaceCharAtIndex index replacement str = strHead ++ replacement : safeTail strAfter
    where (strHead, strAfter) = splitAt index str

One however should be careful: for negative indices, it will replace the first character of the string. For indices that are greater than the length of the string, it will append to the string. Both cases are not per se the desired behavior.

We can alter the behavior by checking for negative indexes, and matching on the empty tail:

replaceCharAtIndex :: Int -> Char -> String -> String
replaceCharAtIndex index replacement str
    | index < 0 = … -- index located before the string
    | (_:xs) <- strAfter = strHead ++ replacement : xs
    | otherwise = …  -- index located after the string.
    where (strHead, strAfter) = splitAt index str

Here we thus have two s to fill in a result for these edge-cases.

Sign up to request clarification or add additional context in comments.

1 Comment

@Zeta: yes, but I personally favor not using drop with a small fixed length, since it is slightly more computationally expensive.
1

In case somebody is looking for such a function or a similar one, I came up with this:

replaceCharAtIndex :: Int -> Char -> String -> String
replaceCharAtIndex index replacement str = strHead ++ [replacement] ++ drop 1 strAfter
  where (strHead, strAfter) = splitAt index str

Usage: replaceCharAtIndex 4 '!' "Hello World!"

2 Comments

this unfortunately will crash if you are trying to replace the very last character in a string. You can avoid this by replacing tail strAfter with drop 1 strAfter
If you use drop 1 strAfter then it will succeed for any positive index, appending the char at the end of the string if the index is too large. Personally I'd go with strHead ++ [replacement] ++ strAfter where (strHead, x:strAfter) = splitAt index str
1

There are 4 cases to consider:

  1. The list is empty or the index is negative.
  2. The index is too big (greater than or equal to the length of the list).
  3. The list is nonempty and the index is 0.
  4. The list is nonempty and the index is not zero.

In case 1, we return the input unchanged:

replace k _ xs | null xs || k < 0 = xs

(This could be split into two subcases

replace _ _ [] = []
replace k _ xs | k < 0 = xs

)

Case 2 is implicitly handled by recursing until we reach Case 1.

Case 3 is handled by simply returning the replacement character prepended to the tail of the input.

replace 0 c (x:xs) = c : xs

Case 4 is the interesting case: replacing the kth element of a list is the same as prepending the head of the list to the result of replacing the k-1th element of the tail.

replace k c (x:xs) = x : replace (k-1) c xs

When k is too big, we eventually reach Case 1, since xs will become null before k becomes 0, bypassing Case 3.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.