I'd like to sort a list of tuples by the third or fourth element (say c or d) in the list of type:
myList = [(a,b,c,d,e)]
I know if the tuple is of type (a,b) I can use the following approach:
mySort xmyList = sortBy (compare `on` snd) x
But the type of sortBy will not work on tuples with length greater than two (so obviously there is no point writing an accessor function for thd or fth) :
(a -> a -> Ordering) -> [a] -> [a]
But the type of sortBy will not work on tuples with length greater
than two
No, it actually works for any list. For instance, if you want to sort on c, just do:
mySort xmyList = sortBy (compare `on` (\(a,b,c,d,e) -> c)) x
or
mySort xmyList = sortBy (comparing thirdOf5) x
where thirdOf5 (_,_,c,_,_) = c
Related
I've got the following code that takes an int value and removes the first n amount of elements in a list.
removeEle :: Int -> [a] -> [a]
removeEle n xs
| ((n <= 0) || null xs) = xs
| otherwise = removeEle (n-1) (tail xs)
How would i append this so that this works on a list of tuples by their second element? etc
[(String1, 50)], [(String2, 600)], [(String3, 10)]
There is not much you can do to amend your current solution so that it removes the first n smallest elements. To be able to remove the first n smallest, you need to have the total ordering of the whole list so that you can decide which elements are in the n smallest interval.
One easy solution is to sort the list and the remove the first n elements. This solution doesn't preserve the original ordering though.
Using soryBy and drop from Data.List you can do the following:
removeNSmallest :: Ord a => Int -> [(String, a)] -> [(String, a)]
removeNSmallest n xs = drop n $ sortBy (\(_, a) (_, b) -> compare a b) xs
As #Micha Wiedenmann pointed out, you can use sortBy (comparing snd) for sorting the tuples.
A small test:
λ> removeNSmallest 1 [("String1", 50), ("String2", 600), ("String3", 10)]
[("String1",50),("String2",600)]
To preserve the original ordering, one solution is to create a separate ordered list of the second elements of the tuple. Then traverse the original list and for each element that is in the ordered list, remove one from the original.
Your original solution for removing the first n elements of a list would be much more readable if you wrote it using drop:
removeEle :: Int -> [a] -> [a]
removeEle n xs = drop n xs
Or if you want to use explicit recursion:
removeEle :: Int -> [a] -> [a]
removeEle _ [] = []
removeEle 0 xs = xs
removeEle n x:xs = removeEle (n-1) xs
I am dealing with small program with Haskell. Probably the answer is really simple but I try and get no result.
So one of the part in my program is the list:
first = [(3,3),(4,6),(7,7),(5,43),(9,9),(32,1),(43,43) ..]
and according to that list I want to make new one with element that are equal in the () =:
result = [3,7,9,43, ..]
Even though you appear to have not made the most minimal amount of effort to solve this question by yourself, I will give you the answer because it is so trivial and because Haskell is a great language.
Create a function with this signature:
findIdentical :: [(Int, Int)] -> [Int]
It takes a list of tuples and returns a list of ints.
Implement it like this:
findIdentical [] = []
findIdentical ((a,b) : xs)
| a == b = a : (findIdentical xs)
| otherwise = findIdentical xs
As you can see, findIdentical is a recursive function that compares a tuple for equality between both items, and then adds it to the result list if there is found equality.
You can do this for instance with list comprehension. We iterate over every tuple f,s) in first, so we write (f,s) <- first in the right side of the list comprehension, and need to filter on the fact that f and s are equal, so f == s. In that case we add f (or s) to the result. So:
result = [ f | (f,s) <- first, f == s ]
We can turn this into a function that takes as input a list of 2-tuples [(a,a)], and compares these two elements, and returns a list [a]:
f :: Eq a => [(a,a)] -> [a]
f dat = [f | (f,s) <- dat, f == s ]
An easy way to do this is to use the Prelude's filter function, which has the type definition:
filter :: (a -> Bool) -> [a] -> [a]
All you need to do is supply predicate on how to filter the elements in the list, and the list to filter. You can accomplish this easily below:
filterList :: (Eq a) => [(a, a)] -> [a]
filterList xs = [x | (x, y) <- filter (\(a, b) -> a == b) xs]
Which behaves as expected:
*Main> filterList [(3,3),(4,6),(7,7),(5,43),(9,9),(32,1),(43,43)]
[3,7,9,43]
I'm trying to map a list of tuples into a different list of tuples with no luck.
Example input:
a = [("eo","th"),("or","he")]
Example output:
[('e','t'),('o','h'),('o','h'),('r','e')]
I have tried:
map (\(a,b) -> (a!!0,b!!0):(a!!1,b!!1):[]) a
but it produces:
[[('e','t'),('o','h')],[('o','h'),('r','e')]]
You have to use concat on your result or use concatMap instead of map. After all, you return lists in your map and therefore get a list of lists.
Let's give your function a name and a type:
magic :: [([Char], [Char])] -> [(Char, Char)]
Now, we can think of this as a two-step process: from every pair in the original list we're going to get a list:
magicPair :: ([Char], [Char]) -> [(Char, Char)]
magicPair (a,b) = zip a b
Now we need to map magicPair over all elements in your original list and concatenate the result:
magic xs = concat (map magicPair xs)
The combination concat . map f is so common that there is a function called concatMap for this:
magic xs = concatMap magicPair xs
And using a function f on a pair instead of two arguments is also common, so magicPair = uncurry zip:
magic xs = concatMap (uncurry zip) xs
We can now remove xs on both sides to end up with the final variant of magic:
magic = concatMap (uncurry zip)
Here is a quick way to give you the output
simplify = (>>= uncurry zip)
So i've been praticing Haskell, and i was doing just fine, until i got stuck in this exercise. Basically i want a function that receives a list like this :
xs = [("a","b"),("a","c"),("b","e")]
returns something like this :
xs = [("a",["b","c"]), ("b",["e"])].
I come up with this code:
list xs = [(a,[b])|(a,b) <- xs]
but the problem is that this doesn't do what i want. i guess it's close, but not right.
Here's what this returns:
xs = [("a",["b"]),("a",["c"]),("b",["e"])]
If you don't care about the order of the tuples in the final list, the most efficient way (that doesn't reinvent the wheel) would be to make use of the Map type from Data.Map in the containers package:
import Data.Map as Map
clump :: Ord a => [(a,b)] -> [(a, [b])]
clump xs = Map.toList $ Map.fromListWith (flip (++)) [(a, [b]) | (a,b) <- xs]
main = do print $ clump [("a","b"),("a","c"),("b","e")]
If you do care about the result order, you'll probably have to do something ugly and O(n^2) like this:
import Data.List (nub)
clump' :: Eq a => [(a,b)] -> [(a, [b])]
clump' xs = [(a, [b | (a', b) <- xs, a' == a]) | a <- nub $ map fst xs]
main = do print $ clump' [("a","b"),("a","c"),("b","e")]
You could use right fold with Data.Map.insertWith:
import Data.Map as M hiding (foldr)
main :: IO ()
main = print . M.toList
$ foldr (\(k, v) m -> M.insertWith (++) k [v] m)
M.empty
[("a","b"),("a","c"),("b","e")]
Output:
./main
[("a",["b","c"]),("b",["e"])]
The basic principle is that you want to group "similar" elements together.
Whenever you want to group elements together, you have the group functions in Data.List. In this case, you want to specify yourself what counts as similar, so you will need to use the groupBy version. Most functions in Data.List have a By-version that lets you specify more in detail what you want.
Step 1
In your case, you want to define "similarity" as "having the same first element". In Haskell, "having the same first element on a pair" means
(==) `on` fst
In other words, equality on the first element of a pair.
So to do the grouping, we supply that requirement to groupBy, like so:
groupBy ((==) `on` fst) xs
This will get us back, in your example, the two groups:
[[("a","b"),("a","c")]
,[("b","e")]]
Step 2
Now what remains is turning those lists into pairs. The basic principle behind that is, if we let
ys = [("a","b"),("a","c")]
as an example, to take the first element of the first pair, and then just smash the second element of all pairs together into a list. Taking the first element of the first pair is easy!
fst (head ys) == "a"
Taking all the second elements is fairly easy as well!
map snd ys == ["b", "c"]
Both of these operations together give us what we want.
(fst (head ys), map snd ys) == ("a", ["b", "c"])
Finished product
So if you want to, you can write your clumping function as
clump xs = (fst (head ys), map snd ys)
where ys = groupBy ((==) `on` fst) xs
I'm having a hard time when I try to order a list of lists by the second element, something like this
list = [[_,B,_,_,_],[_,A,_,_,_],[_,C,_,_,_]]
into this:
list = [[_,A,_,_,_],[_,B,_,_,_],[_,C,_,_,_]]
I've tried:
sortBy compare $ [([1,2]!!1),([2,3]!!1)]
But it filters the seconds elements and order that into [2,3].
What you tried to do is sort the list [([1,2]!!1),([2,3]!!1)], which is equivalent to [2, 3], by compare. What you want to do is use sortBy with a function that first gets the second element and then compares:
sortBySecond = sortBy (\ a b -> compare (a !! 1) (b !! 1))
Then take the list you have and apply this function to it:
sortBySecond [[1, 2], [2, 3]]
You can make this function neater by using on from Data.Function:
import Data.Function
sortBySecond = sortBy (compare `on` (!! 1))
You can also use comparing from Data.Ord:
sortBySecond = sortBy $ comparing (!! 1)
another idea I came up with would be to simply start sorting at the second element of a list by using tail. I also tried to write it point free - just as an exercise for me.
pfsortBySnd :: (Ord a) => [[a]] -> [[a]]
pfsortBySnd = sortBy second
where second = comparing tail
sortBySnd :: (Ord a) => [[a]] -> [[a]]
sortBySnd xx = sortBy second xx
where second x y = compare (tail x) (tail y)