John John
John John

Reputation: 377

F# Sort array of tuples

let standard = (0, 4.5M, 4L)
let tuples = [| ("A", -2, 1.0M, 2L); 
                ("B", -1, 2.0M, 3L); 
                ("C", 0, 3.0M, 4L); 
                ("D", 1, 4.0M, 5L); 
                ("E", 2, 5.0M, 6L) |]
let qualified = tuples
              |> Array.sortBy(fun (_, a, b, c) -> (a, -b, c))
              |> Array.filter(fun (_, a, b, c) -> (a, b, c) <= standard)
printfn "%A" qualified

I have an array of tuples, and a standard. I want to sort the tuples and pick up those tuples which meet the requirements.
For the tuples, I ignore the first element, and sort the second element and forth element in ordinary way, but also sort the third element in reverse order; and I have a standard tuple, for all the tuples with the second element is at least as big as the standard, and the third element is at most as big as the standard are qualified tuples. In the above example, the qualified tuple is: = [| ("C", 0, 3.0M, 4L) |] The conditions are: the second element >= 0 and the third element <= 4.5M and forth element >= 4L But my code did NOT work! Let me know how to write a function can do this job! Thanks and have a nice weekend. John

Upvotes: 1

Views: 1560

Answers (2)

Gus
Gus

Reputation: 26174

Just change the last line for:

Array.filter(fun (_, a, b, c) -> let (x,y,z) = standard in a >= x && b <= y && c >= z)

Note that the tuple ("D", 1, 4.0M, 5L) also qualifies.

UPDATE:

Tomas is right, it's better to filter first. Another interesting functional way to solve it could be making the 3-uple an applicative functor.

let pure' x = (x,x,x)
let (<*>) (f,g,h) (x,y,z) = (f x, g y, h z)

let standard = (0, 4.5M, 4L)
let tuples = [| ("A", -2, 1.0M, 2L); 
                ("B", -1, 2.0M, 3L); 
                ("C", 0, 3.0M, 4L); 
                ("D", 1, 4.0M, 5L); 
                ("E", 2, 5.0M, 6L) |]
let qualified = tuples              
              |> Array.filter(fun (_, a, b, c) -> ((>=),(<=),(>=)) <*> (a,b,c) <*> standard = pure' true)
              |> Array.sortBy(fun (_, a, b, c) -> (a, -b, c))

Upvotes: 2

Tomas Petricek
Tomas Petricek

Reputation: 243051

I don't think there is any clever way to do what you need using the built in comparison that works on tuples. The main problem is that the comparison treats first element as the most significant, so it decides just using the first few elements (without even considering the rest of the values. Your conditions specify restrictions on all elements. So, the solution from Gustavo is probably the easiest way to go.

However, there are some minor points - firstly, it may be a good idea to perform filtering before sorting, because then your sorting function will need to sort fewer elements:

 let qualified = 
   tuples 
   |> Array.sortBy (...) 
   |> Array.filter (...)

If you wanted to represent the condition in some global value that is easy to change, then creating a tuple with 3 values that specify the required minumum/maximum is not enough, because you don't say whether the value is minimum or maximum... However, you could use a tuple of functions that specify the conditions:

let standard = ((fun _ -> true), (<=) 0, (>=) 4.5M, (<=) 4L) 

This specifies that all values of first element are OK, values of the second element should be larger than zero ( (<=) 0 stands for a function that takes x and returns 0 <= x) etc. Then you can write:

let conditionsHold (p1, p2, p3, p4) (v1, v2, v3, v4) = 
  p1 v1 && p2 v2 && p3 v3 && p4 v4

let qualified = 
  tuples 
  |> Array.sortBy(fun (_, a, b, c) -> (a, -b, c)) 
  |> Array.filter (conditionsHold standard)

Upvotes: 3

Related Questions