views:

170

answers:

4

Scala offers a method called stripMargin that removes the left-hand part of a multiline string up to a specified delimiter (default: "|"). Here is an example:

"""|Foo
   |Bar""".stripMargin

returns the string

Foo
Bar

Is there a similar function in Clojure? If not, how would you implement it (most functionally)?

Thanks.

UPDATE: The example I gave is not complete. The stripMargin method also preserves whitespace after the delimiter:

"""|Foo
   |   Bar""".stripMargin

returns the string

Foo
   Bar
+2  A: 

A quick googling didn't return any existing functions, but here is my attempt at the problem. It doesn't support custom delimiters and it is not very optimized speedwise.

(use '[clojure.contrib.str-utils :only (re-split re-sub str-join)])

(defn strip-margin [s]
  (let [lines (seq (re-split #"\n" s))]
    (str-join "\n"
      (for [line lines]
        (re-sub #"^\s*\|" "" line)))))
ponzao
+4  A: 

There is no such function built in but you write it easily:

user=> (use '[clojure.contrib.string :only (join, split-lines, ltrim)]) //'
nil
user=> (->> "|Foo\n  |Bar" split-lines (map ltrim) 
  (map #(.replaceFirst % "\\|" "")) (join "\n"))
"Foo\nBar"
abhin4v
Note that Scala's `stripMargin` works with any delimiter character, but it also expects the character to be the first non-blank character in the line.
Daniel
@abhinv4: see my update above. This seems to support leading whitespace after the delimiter: `(->> "|Foo\n | Bar" split-lines (map #(.replaceFirst % "^\\s*\\|" "")) (join "\n"))` ==> `"Foo\n Bar"`
Ralph
@Ralph: "This seems to support leading whitespace after the delimiter." Isn't that what you want as per your question?
abhin4v
@Daniel: I didn't look in Scala's `stripMargin`. I just used the example given by the OP.
abhin4v
@abhin4v: I did not want to strip whitespace after the delimiter. This function allows me to enter indented XML inside a multiline String and still preserve the indentation.
Ralph
@Ralph: And my answer __does not__ strip whitespace after the delimiter. What is the problem?
abhin4v
@abhin4v: Isn't that what the `ltrim` is for? If not, my mistake. I'm a Clojure noob. I'm not clear on what `->>` does to the result.
Ralph
@Ralph: No. `ltrim` is for striping the whitespace __before__ the delimiter.
abhin4v
@abhin4v: Sorry. I misunderstood. I just re-read the docs on `->>` and it is becoming clearer. Your call translates to `(join "\n" (map #(.replaceFirst % "\\|" "") (map ltrim (split-lines "|Foo\n | Bar"))))`
Ralph
@All: Clojure is *elegant*.
Ralph
+1  A: 

Here my complete solution, gathered from the answers above:

(use '[clojure.contrib.string :only (join, split-lines, ltrim)])

(defn strip-margin
  ([s d] (->> s split-lines (map ltrim) (map #(.replaceFirst % d "")) (join "\n")))
  ([s] (strip-margin s "\\|")))

Here is some "real" sample input and output:

(println
  (strip-margin "|<?xml version='1.0' encoding='utf-8'?>
                 |<people>
                 |  <person>
                 |    <name>Joe Smith</name>
                 |  </person>
                 |</people>"))

==>

<?xml version='1.0' encoding='utf-8'?>
<people>
  <person>
    <name>Joe Smith</name>
  </person>
</people>
nil

Thanks to all contributors.

Ralph
+1  A: 

You can also do this with a single regexp:

(use '[clojure.contrib.string :only (replace-re)])

(def test-string 
"|<?xml version='1.0' encoding='utf-8'?>
                 |<people>
                 |  <person>
                 |    <name>Joe Smith</name>
                 |  </person>
                 |</people>")


(replace-re #"(^\|)|(.+\|)" "" test-string))
Matti Pastell
@Matti: I like this one, however, as Jamie Zawinski is famously known for saying: 'Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.' :-)
Ralph
I agree, I've been working a lot with regexps during the last couple of days and its very easy to shoot yourself in the leg... Although this site helps http://gskinner.com/RegExr/
Matti Pastell