Pei Chuang
Pei Chuang

Reputation: 67

Filter function in haskell

I have two lists

a = [1,2,3,4,5,6]
b = [3,4,5,6,7,8,9]

the result i want to get is the common part of these two lists: [3,4,5,6] I tried to use filter function, but it always output error

filter (== a) b 
filter (== b) a

then combine them together

Upvotes: 1

Views: 2725

Answers (7)

Will Ness
Will Ness

Reputation: 71065

If your lists are built of increasing positive numbers, as they seem to be, you can have an O(n) solution instead of the quadratic intersect and friends:

ordzip :: (Ord a, Num a) => [a] -> [a] -> [(a,a)]
ordzip a b = g a b where 
  g a@(x:r) b@(y:q)  | x < y     = (x,0) : g r b 
                     | y < x     = (0,y) : g a q 
                     | otherwise = (x,y) : g r q 
  g a       []                   = [(x,0) | x <- a]
  g []      b                    = [(0,y) | y <- b]

diff xs ys = [x | (x,y)<- ordzip xs ys, x/=0 && y==0 ]  -- set difference
meet xs ys = [y | (x,y)<- ordzip xs ys, x/=0 && y/=0 ]  --     intersection
joyn xs ys = [z | (x,y)<- ordzip xs ys, let z=max x y]  --     union

The one you want here is meet:

Prelude> meet [1,2,3,4,5,6] [3,4,5,6,7,8,9]
[3,4,5,6]

The above is just a toy code, to give us an idea. Normally, you would use Maybe a to convey the information of a possible value, instead of using the specially separated element, like I do with 0 above. A value of type Maybe a can be either Nothing or Just a.

But actually, we would never have a (Nothing, Nothing) combination, as we choose either one of the two or both in ordzip, but never none, right? So what we really need here is Data.These.These:

import Data.These

ordzip :: (Ord a) => [a] -> [b] -> [These a b]
ordzip a@(x:t) b@(y:r) = case compare x y of
                            LT -> This  x   : ordzip t b 
                            GT -> That    y : ordzip a r 
                            EQ -> These x y : ordzip t r 
ordzip a       []      = map This a
ordzip []      b       = map That b

diff = catThis .: ordzip 
meet = map snd .: catThese .: ordzip
joyn = map (these id id const) .: ordzip
-- infixr 8 .: ; (f .: g) x y = f (g x y)

Re-writing these three set functions by merging their definitions with the ordzip above can lead to more efficient code in some cases (e.g. there's no need to attach the extra tails with ordzip a [] or ordzip [] b just so they are discarded in meet). Moreover, it would made meet (or diff) properly productive when one of its argument lists is infinite and the other is finite. The three definitions above are more of an executable specification.

Also sometimes we might need to decide the equality with some additional predicate, not just plain compare, and we might construct the elements of the result differently, as we might need them (e.g. including both x and y in the results in meet).

These functions work with ordered lists. There's also the data-ordlist package for this with much more tools in it.

Upvotes: 0

Ori
Ori

Reputation: 154

intersect a b = [x| x <- a, x `elem` b]

Using list a argument as a generator in x <- a. The Boolean expression x elem b filters each element. If True added to x else discarded.

Upvotes: 1

user3498646
user3498646

Reputation: 25

Here's a one-liner

[x | x <- a, y <- b, x == y ]

Upvotes: 1

leftaroundabout
leftaroundabout

Reputation: 120711

While you can directly use the intersect function on lists, I should recommend you use a more efficient, dedicated container instead of lists. If you really mean to express sets, then why don't you use Data.Set?

Prelude> import qualified Data.Set as Set
Prelude Set> let a = Set.fromList [1,2,3,4,5,6]; b = Set.fromList [3,4,5,6,7,8,9]
Loading package array-0.5.0.0 ... linking ... done.
Loading package deepseq-1.3.0.2 ... linking ... done.
Loading package containers-0.5.5.1 ... linking ... done.
Prelude Set> Set.intersection a b
fromList [3,4,5,6]

This is O(n ⋅ log n) rather than O(n2), much faster for large sets. Also, it expresses your intention much better if you use a dedicated set type: it makes it clear that you count out duplicates, and don't have any nontrivial ordering of the elements.

The downside is, the elements need to be Ord because the implementation uses the canonical ordering. An alternative would be HashSet.

Upvotes: 1

RafaelCaballero
RafaelCaballero

Reputation: 1613

If you look just for common elements use intersect from Data.List

a `intersect` b == [3,4,5,6]

A related question: is there union and intersect Haskell Prelude implementation?

Upvotes: 4

Adi
Adi

Reputation: 2394

you need to use intersect function to take common elements from two list.

See how to use intersect function on lists.

Upvotes: 2

Sibi
Sibi

Reputation: 48644

You need to iterate one list using filter and check if the elements from that list is present in the other list using the elem function:

inte :: Eq a => [a] -> [a] -> [a]
inte a b = filter (\x -> x `elem` a) b

Another way is see if there is any built-in library function present. As a first step, write the type signature for the function you want:

[a] -> [a] -> [a]

Then run a hoogle query to see if there is any prebuilt function for that. There is an intersect function in Data.List which achieves exactly your objective.

Upvotes: 6

Related Questions