1

I want to modify a string by applying a function to some of its chars (by starting index and length).
For example, I want to increment the ascii representation of the string "aaaaa" from the 2nd index to the 4th.

[start=1 length=3]
"aaaaa" => "abbba"

The only way I could think of is applying map, but it goes over all the sequence.

4 Answers 4

2

You could use subs to get the portions you do and don't want to modify. After modification use str to concatenate the result together:

(defn replace-in-str [f in from len]
  (let [before (subs in 0 from)
        after (subs in (+ from len))
        being-replaced (subs in from (+ from len))
        replaced (f being-replaced)]
    (str before replaced after)))

You can call it:

(replace-in-str 
 (fn [sub-str] (apply str (map #(char (+ 1 (int %))) sub-str))) 
 "aaaaa" 
 1 
 3)
Sign up to request clarification or add additional context in comments.

4 Comments

Yea I though of doing it, but it sounds like an awful lot of work, what do you think about my solution? (described in my answer)
I wouldn't reach for map-indexed, even if it wasn't string substitution being done - in that case I'd use subvec.
Yes it is clear for replace, but what about applying a function?
Changed the answer so it now calls your example function (called within another function that allows your function to be called every character of the string).
1

Indeed map applies the function to every element in the sequence. One way to get around that is to start with map-indexed. Unlike map, map-indexed passes the element's index as the first argument to the mapping function. When we have element's index, we can use it to choose if we need to perform the operation or just return the element as is.

A solution might look like this:

(defn inc-char [c]
  (char (inc (long c))))

(defn if-in-range [from to f] 
  (fn [i x & args] 
    (if (<= from i (dec to)) 
      (apply f x args)
      x)))

(defn map-subs [from to f s]
  (apply str (map-indexed (if-in-range from to f) s)))

(map-subs 1 4 inc-char "aaaaa")
;; "abbba"

1 Comment

Yes that was what I also thought of, although I would think clojure had something built-in to handle that...
0

I thought of using map-index to execute the operation only on the specified index:

((fn [op start length] (map-indexed (fn [i m] (if (<= start i length)
                         (op m)
                         m)) "aaaaa"))
  #(char (+ 1 (int %)))
  1
  3)

=> (\a \b \b \b \a)

1 Comment

You can wrap the end result with (apply str ) to turn it back into a String btw.
0

Here you go:

(defn replace-str
  [s start-i end-i]
  (apply str (map-indexed (fn [index val]
              (if (and (>= index start-i)
                       (<= index end-i)) 
                  (char (+ (int val) 1))
                  val))
                s)))

(replace-str "aaaa" 1 2)
;=> "abba"

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.