Search code examples
javascripttypescriptrecursioncomparatorblocking

blocking recursive call when using two comparators


I have a list of items of type PartiePos ... simply with that scheme:

{
    ID: string
}

Now, I have two further lists of items of type string.

Here all three lists:

items = [{ID: "123"}, {ID: "234"}, {ID: "345"}, {ID: "456"}, {ID: "567"}]
assigned = ["123", "345"]
disabled = ["567", "234"]

I want to sort them according to this scheme:

  • Always keep initial order of items
  • If in assigned, put it to the end but keep initial order of items
  • If in disabled, put it to the end behind assigned, but keep initial order of items

Would resolve my lists into this sorted output:

"456"
"123"
"345"
"234"
"567"

I achieve this by applying these two comparators to my list sorting:

const comparatorA = (a: PartiePos, b: PartiePos) => {
    if (assigned.includes(a.ID) && assigned.includes(b.ID)) {
        return 0
    }
    if (assigned.includes(a.ID) && ! assigned.includes(b.ID)) {
        return 1
    }
    if (! assigned.includes(a.ID) && assigned.includes(b.ID)) {
        return -1
    }
}

const comparatorD = (a: PartiePos, b: PartiePos) => {
    if (disabled.includes(a.ID) && disabled.includes(b.ID)) {
        return 0
    }
    if (disabled.includes(a.ID) && ! disabled.includes(b.ID)) {
        return 1
    }
    if (! disabled.includes(a.ID) && disabled.includes(b.ID)) {
        return -1
    }
}

[...]

return items.sort(comparatorA).sort(comparatorD)

But the sorting is quite slow and blocks my site, the dev console tells me that I have a recursive mutation and this causes blocking of javascript.

Any idea how to improve this?


Solution

  • There is no need to sort here since that would take O(n log n) time. You can filter out the disabled and assigned items, then concat just those items:

    const items = [{ID: "123"}, {ID: "234"}, {ID: "345"}, {ID: "456"}, {ID: "567"}];
    // If these are long, use a JS `new Set` object for fast lookup;
    const assigned = ["123", "345"];
    const disabled = ["567", "234"];
    // just those that keep their regular place
    const withoutAssignedAndDisabled = items.filter(x => !assigned.includes(x.ID) && !disabled.includes(x.ID));
    // concat the assigned and then disabled
    const result = withoutAssignedAndDisabled.concat(
      items.filter(x => assigned.includes(x.ID))
    ).concat(
      items.filter(x => disabled.includes(x.ID))
    );