How would I filter a list so that I only return the list of those that are integers?
For example, filtering a list like [1, 1.2, 2, 2.2]
would return [1, 2]
.
Considering your list to be of type [Double]
as you can not have (in any simple way) a list with elements of different types.
Once you have a list of double, you can use the function ceiling
.
ceiling 2.1 = 3
ceiling 2.0 = 2
so a function to check if a number has no fractional part can be written as
nonFractional d = (fromIntegral $ ceiling d) == d
now you can do filter on this
> filter nonFractional [1, 1.2, 2, 2.2]
[1.0,2.0]
(Edit) The above approach of comparing equality does not work for large numbers like
> nonFractional (12345678987654321.5)
True
Using @David's idea if you change the definition of nonFractional
as
nonFractional d = (fromIntegral $ ceiling d :: Rational) == d
Then it seems to work for large fractions as well
> nonFractional (12345678987654321.5)
True
First of all, your list should be homogenous, so you can't have list of Integer
s and Doubles
.
There is a nice function properFraction
, which decomposes a number into its whole and fractional parts:
properFraction :: (Fractional a, Integral b) => a -> (b,a)
So, we can define a function to figure out is number have a non-zero fractional part or not.
> let haveNoFractionalPart = (== 0.0) . snd . properFraction
haveNoFractionalPart :: Double -> Bool
No we can filter your list with that function:
> filter haveNoFractionalPart [1, 1.2, 2, 2.2]
[1.0,2.0]
Update :
I should admit that my solution isn't valid and workable for some cases in the real world. Because of something like
> properFraction (11111111111111111111.1)
(11111111111111110656,0.0)
Anyway, it's hard to imagine the case when it's needed to filter what you calling an Integer
from some list of values that you have. And there is no such way to define that any number with floating point have zero floating part with 100% probability.
Maybe some wrapper over Integer
and Double
will be helpful.
What about this:
filterInt :: (RealFrac a) => [a] -> [Integer]
filterInt [] = []
filterInt (x:xs)
| frac == 0 = a : filterInt xs
| otherwise = filterInt xs
where
(a, frac) = properFraction x
test:
> let li = [1, 1.2, 2, 2.2]
> filterInt li
> [1,2]
A number of solutions have been posted for Rational
, where in actuality you really only need to compare the denominator to 1:
hasFraction' :: Rational -> Bool
hasFraction' = (/= 1) . denominator
This can be generalized to any Real
and is one of the safest methods to check whether a number has a fractional part:
hasFraction :: (Real a) => a -> Bool
hasFraction = hasFraction' . toRational
That function does not solve the rounding error problem, but that's natural. When rounding errors bother you, you're using the wrong data type.
It depends where you got the data from.
Haskell doesn't let you mix pure integers with non-integers, so your integers will get tainted with the inaccuracy inherent in data types like Double
unless you use something more accurate like Rational
, but given that you don't want the non-integers anyway, throw them away at source, before they're numeric data, if you can.
getInt
below. getInt
below. getInt
converts a String to an Integer, cunningly ignoring anything that isn't an Integer:
import Data.Char (isDigit)
getInt :: String -> Maybe Integer
getInt xs | all isDigit xs = Just (read xs)
| otherwise = Nothing
So getInt "12345"
is Just 12345
whereas getInt 12345678987654321.1
is Nothing
. We can use that to remove non-integer input from some list:
getInts :: [String] -> [Integer]
getInts xss = catMaybes $ map getInt xss
or more consisely, we could write
getInts = catMaybes.map getInt
.
Now catMaybes :: [Maybe a] -> [a]
and it gets rid of the Nothing
s and unwraps the Just
s. We'll need to
import Data.Maybe (catMaybes)
at the top to get it.
If your data comes as a floating point number of some sort, bear in mind there's no true equality in a floating point type, so even if you convert to a more accurate representation before checking, it's logically impossible for you to ever know whether the original data represented an exact integer or just something quite close to an integer that the floating point representation rounded before the data got to you. For example:
Prelude> (12345678987654321.6 :: Double) == 12345678987654322.0
True
whereas
Prelude> (12345678987654321.6 :: Rational) == 12345678987654322.0
False
But if you can choose the data type, you're in control of the generating code, so choose not to include non-integers!
Summary: it's easiest to get rid of non-integers before you turn them into numerical data, and you're not subject to occasional bizzare rounding errors.
Your list will have to be of type [Double]
or [Integer]
, or some other type of number. You cannot mix types.
That said, if you have a list of doubles and you're trying to filter out those that are not integers, you can always use round
, floor
, or ceiling
to check equivalency to the number.
For example:
isInt :: (RealFrac a) => a -> Bool
isInt x = x == (fromIntegral $ round x)
Then you can just filter your data using this, using filter
:
filter isInt [1, 1.2, 2, 2.2] -- [1.0, 2.0]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.