0

Assuming I have a list like this

let a =[["Warning:","Route","1543","must","Stop","on","Link","11881"],["Warning:","Route","1578","must","Stop","on","Link","12171"]]

And I want to extract third element of each list inside it, i.e I want to get the resultant as ["1543","1578"]

I wrote the following piece of code for obtaining it, but it is not working:

foldr (\acc x -> (x !! 2):acc) [] a
5
  • 8
    map (!! 2) would suffice Commented Dec 31, 2012 at 10:28
  • Ok, thanks :-). That's very clean. But why isn't the above code working? Commented Dec 31, 2012 at 10:30
  • 2
    Also, foldr works too, but it requires other order of arguments: foldr (\x acc -> (x !! 2):acc) [] a. Commented Dec 31, 2012 at 10:36
  • 3
    You should be careful while using the above solution as it can totally break when any sublist contains less than 3 elements. A solution using Maybe is a more safer choice. Commented Dec 31, 2012 at 11:38
  • Like Satvik said, this is not particularly safe regardless of how you implement it. It might be best to not create those nested lists at all, but use a specialised data type and write a nice parser with e.g. parsec – but it would probably be overkill; if this is just for a one-time script then map (!! 2) is positively fine. Commented Dec 31, 2012 at 13:22

2 Answers 2

3

Here's a safe way to do what you want to do using a list comprehension and pattern matching

let a' = [x | (_:(_:(x:_))) <- a]

This will iterate over the list a, look at all sublists of length at least 3, and return the third element of each such sublist.

This solution will silently ignore sublists of length less than 3. To work around this, and gain some flexibility, you can write a function that uses Maybe in its return type; if the list has a third element, it will return Just the third element, otherwise it will return Nothing.

takeThird :: [a] -> Maybe a
takeThird (_:(_:(x:_))) = Just x
takeThird _             = Nothing

Then, you can write a safe version of your operation using a list comprehension:

let safea = [takeThird x | x <- a]

And from that you can create a version that may drop elements:

let maybedropa = [x | Just x <- safea]
Sign up to request clarification or add additional context in comments.

Comments

0

An alternative solution using the recent lens library (Control.lens) would:

import Control.Lens
>a^..traverse.(element 2)
["1543","1578"]

Not using the infix notation:

>toListOf (traverse.(element 2)) a
["1543","1578"]

'traverse' visits each element of a traversable, in this case the list, and (element 2) grabs the element with the 2 index and toListOf gathers it all into a list. This method has the advantage that if decide to complicate your data structure later you just need to write your own lens interface to it if it does not already have one.

Let say instead of a [[String]] you have [[(Annotation, String)]] and you just want the String portion just like above. Then all you need to do is the _2 tuple accessor.

a^..traverse.(element 2)._2

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.