How can I sort contracts according to its data fields?

G-d willing

Hello,
I have a list of contracts that defines offer agreements.
I need to sort the agreements according to the price stated in the agreement.

template OfferAgreement
with
buyer: Party
seller: Party
price: Decimal
where
signatory buyer
observer seller

I also have a contract that has the list of all the PurchaseAgreement contracts (stored in offers property):

template GroupedAgreements
with
seller: Party
offers: [ContractId OfferAgreement]
where
signatory seller

choice GetHigherOffer: ContractId OfferAgreement
  controller seller
    do
      let sortedOffers = sortBy comparator offers

comparator: ContractId OfferAgreement ā†’ ContractId OfferAgreement ā†’ Ordering
comparator a b = do
offer1 ā† fetch a
offer2 ā† fetch b

if offer1.price < offer2.price then LT
else if offer1.price < offer2.price then GT
else EQ

I canā€™t get this to compile, since I am getting an error on the line:
offer1 ā† fetch a
saying:
Couldnā€™t match expected type ā€˜Orderingā€™ with actual type ā€˜Update b1ā€™

Can you please help me fix this issue?

@cohen.avraham fetch is an impure function because it looks up your data on the ledger. You need to perform the lookup outside of your comparator:

offers' <- forA offers fetch                 # Impure, IO side effect
let sortedOffers = sortBy comparator offers' # Pure, no side effects

Also my personal stylistic nit is to always name ContractIds with an Id or Ids suffix. This way you can avoid the ' and write

offers <- forA offerIds fetch
1 Like

Thank you very much. And, using this approach, how can I know what is the contract id of every offer? I need the id in order to revoke contracts that I donā€™t want to fulfill

You can retain the ContractId as part of a pair

offersAndIds <- forA offers (\offerId -> do
  offer <- fetch offerId
  return (offerId, offer))

and ignore the ContractId portion in your comparator.

I canā€™t get it to work, here is my code:
offersAndIds ā† forA offers (\offerId ā†’ do
offer ā† fetch offerId
return (offerId, offer))
let sortedBids = sortBy comparator offersAndIds

And the code of the comparison function is:
comparator: (ContractId CustomerOffer, CustomerOffer) ā†’ (ContractId CustomerOffer, CustomerOffer) ā†’ Ordering
comparator a b =
do
let
offer1 = snd a
offer2 = snd b
if offer1.value < offer2.value then LT
else if offer1.value < offer2.value then GT
else EQ

When I log the 2 lists, I am getting the same results and the list is not sorted by the value.
Any idea?

I think thereā€™s a bug in your comparator, the else clause has the same condition as the first if:

     if offer1.value < offer2.value then LT
else if offer1.value < offer2.value then GT

You should probably avoid writing these custom comparison functions and use the built in compare function. I would write:

comparator a b = compare a._2.value b._2.value

Then you can observe that thereā€™s a sortOn function, so you can avoid writing a comparator entirely and just write

let sorted = sortOn (\p -> p._2.value) offersAndIds

@Leonid_Rozenberg You have good eyes :crazy_face:

Regarding the restā€¦ thank you very much. it fixed my problem, and I also learned a new thing in Daml :+1:

1 Like