Dataweave: Create ranking from scores

Viewed 128

With the following input:

[5,5,4,4,4,2,2,1]

I want to generate the following output:

[
  {
    "points": 5,
    "rank": 1
  },
  {
    "points": 5,
    "rank": 1
  },
  {
    "points": 4,
    "rank": 3
  },
  {
    "points": 4,
    "rank": 3
  },
  {
    "points": 4,
    "rank": 3
  },
  {
    "points": 2,
    "rank": 6
  },
  {
    "points": 2,
    "rank": 6
  },
  {
    "points": 1,
    "rank": 8
  }
]

I found a solution (see the response) but I wonder if is there a better way to do it.

4 Answers

I am not sure if this covers all your test cases, but here's a shorter version of code that I came up with-

%dw 2.0
import * from dw::core::Arrays
output application/json
var inp=[5,5,4,4,4,2,2,1]
---
inp map (v0,k0) ->
{
    points:v0,
    rank:indexOf(inp,v0)+1
}

Please let me know what you think.

Sebastien, I think I understood the algorithm. I am not sure whether my solution is simpler but it is done with a single reduce. Give it a try:

%dw 2.0
output application/dw
var data = [5,5,4,4,4,2,2,1]
---

(data reduce (e, acc = {idx: 0, result: []}) -> do {
    var i = acc.idx + 1
    var last = if (isEmpty(acc.result)) {points: e, rank: 1} else acc.result[-1]
    ---
    {
        idx: i,
        result: acc.result + {points: e, rank: if (last.points == e) last.rank else i}
    }
}).result

Assuming, my algorithm is correct, without using reduce,

%dw 2.0
output application/json
import * from dw::core::Arrays

var payload = [5,5,4,4,4,2,2,1]

fun getRank(inputArray, value) = 
using (rankArray = payload orderBy $ map $$ + 1)
rankArray[indexOf(inputArray, value)]
---

payload map (value) -> {
    "points" : value,
    "rank": getRank(payload, value)
}

What the function does is arrange the input array in descending order and save the index + 1 to set the ranking positions (rankArray). Then using the index of the first occurrence of the value from the original array, get the index from the rankArray and that would be the rank. This will result to

[
  {
    "points": 5,
    "rank": 1
  },
  {
    "points": 5,
    "rank": 1
  },
  {
    "points": 4,
    "rank": 3
  },
  {
    "points": 4,
    "rank": 3
  },
  {
    "points": 4,
    "rank": 3
  },
  {
    "points": 2,
    "rank": 6
  },
  {
    "points": 2,
    "rank": 6
  },
  {
    "points": 1,
    "rank": 8
  }
]

Here is something working but I am creating a string to split it in elements:

%dw 2.0
output application/json
import * from dw::core::Strings

var points =[5,5,4,4,4,2,2,1]


var ranks = ((points reduce 
        (i,acc=[]) -> acc + 
                    (if (acc[-1] == null )  [i,true] 
                    else                    [i,acc[-1][0]-i !=0]))

    reduce (i, acc=[[0,1]]) -> acc + 
            (
                if (i[1]==true) [(acc[-1][1]+acc[-1][0]),1           ] 
                else            [acc[-1][0]             ,acc[-1][1]+1]
            ))[1 to -1]
    reduce (i,acc=[]) -> acc + i[0] 

---

(points zip ranks) map 
{
    points: $[0],
    rank:   $[1]
}
Related