Zip two lists in clojure into list of concatenated strings - list

Instead of zip-mapping two lists to get:
(zipmap ["a","b","c"] ["c","d","e"]) = {"c" "e", "b" "d", "a" "c"}
I want to concatenate the first element of the first list with the first element of the second list and so on to get:
("ce","bd","ac")
or in the reverse order.

You can do that with map. map can take multiple collections, it takes the next element from each collection and passes them into the function passed as the first argument (stopping when one of the collections runs out). So you can pass in a function that takes n arguments, and n collections.
The expression
(map str ["a" "b" "c"] ["c" "d" "e"])
will call str first with "a" and "c", then with "b" and "d", then with "c" and "e". The result will be
("ac" "bd" "ce")
Since str can takes a variable number of arguments it can be used with any number of collections. Passing in four collections, like
(map str ["a" "b" "c"] ["d" "e" "f"] ["g" "h" "i"] ["j" "k" "l"])
will evaluate to
("adgj" "behk" "cfil")

Related

How do i find the corresponding index for these two vectors?

In clojure I'm trying to get my order-finder vector to find the index of a name in the names list:
(def customer-names ["Adam" "Beth" "Chloe" "Daniel" "Nathan" "Olivia"])
To then use that index to find the corresponding pizza orders in this vector
(def pizzas ["P" "M" "P" "C" "P" "P" "S" "C" "M" "M" "S" "C"])
Where by each person in the list will have 2 pizzas per order (in the same order as the customer list)
(defn order-finder [customer-names][pizzas]
customer-names(.indexOf v "Adam")
(.indexOf (pizzas = (.indexOf (customer-names * 2)))))
As I'm new to this language i wonder if it is even possible to do it this way?
I suppose your actual requirement is to lookup the pizzas by customer name. And getting .indexOf is just an interim step - which you would skip to make things simpler.
(def customer-names ["Adam" "Beth" "Chloe" "Daniel" "Nathan" "Olivia"])
(def pizzas ["P" "M" "P" "C" "P" "P" "S" "C" "M" "M" "S" "C"])
;; each customer has 2 pizzas - so the pizzas array can be re-arranged like:
;;
(partition 2 pizzas)
;; => (("P" "M") ("P" "C") ("P" "P") ("S" "C") ("M" "M") ("S" "C"))
;; now the order finder is just a `Cojure` map with keys as
;; customer names, values as the pizzas ordered
;;
(def order-finder
(zipmap customer-names (partition 2 pizzas)))
;; => {"Adam" ("P" "M"), "Beth" ("P" "C"), "Chloe" ("P" "P"), "Daniel" ("S" "C"), "Nathan" ("M" "M"), "Olivia" ("S" "C")}
;; Now to find pizzas by customer name, just look it up from the order-finder map:
;;
(order-finder "Adam")
;; => ("P" "M")

Comparing two strings and returning the number of matched words

I'm fairly new to Clojure, and in programming, in general.
Is there a way I can compare two strings word by word and then return the number of matched words in both strings? Also how can I count the numbers in a string?
Ex:
comparing string1 "Hello Alan and Max" and string2 "Hello Alan and Bob" will return "3" (such as Hello Alan and are the words matched in both strings)
and finding the number of words in string1 will result in the number 4.
Thank you
Let's break it down into some smaller problems:
compare two strings word by word
First we'll need a way to take a string and return its words. One way to do this is to assume any whitespace is separating words, so we can use a regular expression with clojure.string/split:
(defn string->words [s]
(clojure.string/split s #"\s+"))
(string->words "Hello world, it's me, Clojure.")
=> ["Hello" "world," "it's" "me," "Clojure."]
return the number of matched words in both strings
The easiest way I can imagine doing this is to build two sets, one to represent the set of words in both sentences, and finding the intersection of the two sets:
(set (string->words "a b c a b c d e f"))
=> #{"d" "f" "e" "a" "b" "c"} ;; #{} represents a set
And we can use the clojure.set/intersection function to find the intersection of two sets:
(defn common-words [a b]
(let [a (set (string->words a))
b (set (string->words b))]
(clojure.set/intersection a b)))
(common-words "say you" "say me")
=> #{"say"}
To get the count of (matching) words, we can use the count function with the output of the above functions:
(count (common-words "say you" "say me")) ;; => 1
what you need to do, is to compare word sequences' items pairwise, and count the number of items until the first mismatch. Here is an almost word for word translation of this:
(defn mismatch-idx [s1 s2]
(let [w #"\S+"]
(->> (map = (re-seq w s1) (re-seq w s2))
(take-while true?)
count)))
user> (mismatch-idx "a b c" "qq b c")
;;=> 0
user> (mismatch-idx "a b c" "a x c")
;;=> 1
user> (mismatch-idx "a b c" "a b x")
;;=> 2

What is the Clojure equivalent of Scala's zipWithIndex?

Scala Seq has the zipWithIndex method:
def zipWithIndex[A1 >: A, That](implicit bf: CanBuildFrom[Seq[A], (A1, Int), That]): That
Zips this sequence with its indices.
returns: A new sequence containing pairs consisting of all elements of this sequence paired with their index. Indices start at 0.
Example: List("a", "b", "c").zipWithIndex = List(("a", 0), ("b", 1), ("c", 2))
What is the equivalent function in Clojure?
Clojure's map-indexed will give you an indexed list of elements in a collection.
user=> (map-indexed vector "foo")
([0 \f] [1 \o] [2 \o])
As #jmargolisvt answered map-indexed is a good solution.
As your example got index inverted with collection items, you can always compose with reverse:
(map-indexed (comp reverse list) ["a", "b", "c"])
Or map over two sequences:
(map list ["a", "b", "c"] (range))

clojure: how to get several items by index out of vector

I am using the following code to extract data by index of [1 2], is there any shorter solution?
(vec (map #(nth ["a" "b" "c"] % ) [1 2]))
mapv maps into a vector, and vectors when applied as functions do index lookup
(mapv ["a" "b" "c"] [1 2])
If you want ONLY the first and second index of a vector, there are many ways...
A simple sub vector can be used to persist the first index until the third index.
(subvec ["a" "b" "c"] 1 3)
You can map the vector and apply your vector to the first and second index to return the last two indices as a vector.
(mapv ["a" "b" "c"] [1 2])
Using the thread-last macro, you can take 3 indices and drop the first.
(->> ["a" "b" "c"] (take 3) (drop 1))
If you have a vector defined with n indices, and all you need is the last n indices, drop base 0 to return the last n.
(drop 1 ["a" "b" "c"])

Insert string every nth element in a list of strings

I'm new to Clojure.
I'm developing a tic tac toe game
I'm trying to make a function that "formats" the board, which is a map with the number of the position as key and the keywords :x :o and :e for values (:e stands for empty).
I want to insert a newline character every 3 in the list of the name of the keywords.
For example "x" "x" "x" "e" "e" "e" "e" "e" "e" should be converted to "x" "x" "x" "\n" "e" "e" "e" "\n" "e" "e" "e" then I would concatenate those strings so I can print it.
(defn- newline-every
[n list]
(if (empty? list)
[]
(let [[fst snd] (split-at n list)]
(concat
(conj fst "\n")
(newline-every n snd)))))
It's Clojure so there are surely many ways to do this in one line. Here's one attempt:
(flatten (interpose "\n" (partition n list))))
As user amalloy commented, there's never an excuse to use flatten, so here's a better way:
(apply concat (interpose ["\n"] (partition n list))))
Which gives, starting from the sequence of strings (which all contain one character) you gave:
... > (newline-every 3 ["x" "x" "x" "e" "e" "e" "e" "e" "e"])
("x" "x" "x" "\n" "e" "e" "e" "\n" "e" "e" "e")
You can then transform that into a string:
... > (apply str (newline-every 3 ["x" "x" "x" "e" "e" "e" "e" "e" "e"]))
"xxx\neee\neee"