user11115921
user11115921

Reputation:

Haskell combine the elements of 2 lists at different index's

Apologies for the awful title, I'm not too sure how to describe it in words but here is what I mean. If you know a better way to phrase this please let me know.

Suppose I had 2 lists, of equal length.

[a, b, c] [x, y, z]

I want to create the list

[[a, y, z], [b, x, z], [c, x, y]]

Essentially for every element of list1, I want the 2 elements at different indexes to the first element in list2.

so for "a" at index 0, the other 2 are "y" at index 1 and "z" at index 2.

I'm pretty sure I know how to do it using indexes, however, I know that that's not very efficient and wanted to see if there is a more functional solution out there.

Thank you.

Upvotes: 0

Views: 494

Answers (3)

Daniel Wagner
Daniel Wagner

Reputation: 153342

I would do it using zippers. Here's a function I've written into so many projects I've memorized it:

zippers :: [a] -> [([a], a, [a])]
zippers = go [] where
    go b [] = []
    go b (h:e) = (b, h, e) : go (h:b) e

(This actually returns a bit more information than we technically need for this application. But it is the general form -- useful in many situations where the restricted version that only returned the prefix/suffix pair and omitted the current focus would sometimes not be enough.)

With this tool in hand, we can just zip (different kind of zip!) together the values from the one list with the zippers of the other list.

combine :: [a] -> [a] -> [[a]]
combine xs ys = zipWith (\x (b, h, e) -> reverse b ++ [x] ++ e) xs (zippers ys)

Try it out in ghci:

> combine "abc" "xyz"
["ayz","xbz","xyc"]

Upvotes: 2

DDub
DDub

Reputation: 3924

You haven't described any edge cases, but you can get the basic behavior you're looking for with something like:

import Data.List (inits, tails)

combine :: [a] -> [a] -> [[a]]
combine xs ys = zipWith3 go xs (tails ys) (inits ys)
  where
    go a (_:xs) ys = a:ys ++ xs
    go _ _ _ = []

The key is that tails returns successive suffixes of its list starting with the full list, and inits returns successive prefixes starting with the empty list.

Upvotes: 2

SergeyKuz1001
SergeyKuz1001

Reputation: 875

You can try this:

\xs ys -> zipWith3 (((++) .) . (:)) xs (init $ inits ys) (tail $ tails ys)

Upvotes: 0

Related Questions