4

This is more of an XQuery than MarkLogic. I have three map:map and each map has key-value pair of "id" and score. I would like to sort all the distinct ids based on the score from each maps. For eg:

map1 : 1:2048, 5:2000
map2 : 2:5000, 1:1000, 4:3000
map3 : 6:100, 7:5000, 2:2000

In the above example, each map is id:score for key value (did not know how to represent here :))..

I want the sorted list of id from three maps based on score..

Is there a good way or better way of doing the sorting, or do I have to union the keys of the map and iterate the sequence of keys and sort them ?

Dave Cassel
  • 8,352
  • 20
  • 38
Ravi
  • 1,179
  • 6
  • 13
  • Are you looking for one combined score across maps? If so, what score do you use when the same key appears multiple times (as with keys 1, 2)? – Dave Cassel May 03 '16 at 19:22
  • If the keys conflict across the maps.. I want to pick the highest score... In the example above the 1 from map1 will be used for sorting – Ravi May 03 '16 at 19:28
  • 2
    Please post the expected output given those sample map data – har07 May 03 '16 at 22:53

1 Answers1

4

This seems like a great use case for folding. Its part of Xquery 3.0 spec.

Folding can go through a sequence of items and gets the result for each item as it goes through. In this example $combinedMaps is the result of the last call and $mapToMerge is the item in the sequence it is currently going through.

Here an example of what you would want to do.

   declare function local:sortMaps(
  $newMap as map:map,
  $mapA as map:map,
  $mapB as map:map
) as map:map {
  let $build :=
    for $key in map:keys($mapA)
    let $otherMapValue :=
      (map:get($mapB, $key), 0)[1]
    let $value := map:get($mapA, $key)
    return 
      if ($value gt $otherMapValue) then (
        map:put($newMap, $key, $value)
      ) else (
        map:put($newMap, $key, $otherMapValue)
      )
  return $newMap
};

let $map1 := 
  map:new((
    map:entry("1",2048),
    map:entry("5",2000)
  ))

let $map2 := 
  map:new((
    map:entry("2",5000),
    map:entry("1",1000),
    map:entry("4",3000)
  ))

let $map3 := 
  map:new((
    map:entry("6",100),
    map:entry("7",5000),
    map:entry("2",2000)
  ))

let $maps := ($map1, $map2, $map3)
return
  fn:fold-left(
    function($combinedMaps, $mapToMerge) {
      let $newMap := map:map()
      let $newMap := local:sortMaps($newMap, $combinedMaps, $mapToMerge)
      let $newMap := local:sortMaps($newMap, $mapToMerge, $combinedMaps)
      return $newMap
    }, 
    $maps[1], 
    $maps
  )
Tyler Replogle
  • 1,339
  • 7
  • 13
  • I did something similar too, i union all maps and do the order by for the map.. But it is the same idea – Ravi May 04 '16 at 17:22