Frequency Queries hackerrank
Optimizations

You should think of your algorithm as a black box. As long as input and output match, internally the function has not to reproduce the textual representation of the problem.

With this in mind you can simplify your code. You also have an approach already in the charToObj function. I would use this to solve the whole problem:


create a map
store values in that map and increase their amount, when they reappear
decrease amount, if necessary
test the map for given frequencies


This has a few advantages:


shorter code, easier to follow
no helper function necessary
no need to call splice
no inner loops anymore
no need to build obj over and over again


The new functions could look like this:



function freqQuery(queries) {
    const frequencies = [];
    const result = [];

    for (const query of queries) {
        const action = query[0];
        const value = query[1];

        if (action === 1) {
            if (typeof frequencies[value] === 'undefined') {
                frequencies[value] = 1;
            } else {
                ++frequencies[value];
            }
        }

        if (action === 2) {
            if (frequencies[value]) {
                --frequencies[value];
            }
        }

        if (action === 3) {
            result.push(frequencies.indexOf(value) === -1 ? 0 : 1);
        }
    }

    return result;
}

console.log(
    freqQuery([[1,1],[2,2],[3,2],[1,1],[1,1],[2,1],[3,2]]),
    freqQuery([[3, 4], [2, 1003], [1, 16], [3, 1]]),
    freqQuery([[1, 3], [2, 3], [3, 2], [1, 4], [1, 5], [1, 5], [1, 4], [3, 2], [2, 4], [3, 2]]),
    freqQuery([[1, 5], [1, 6], [3, 2], [1, 10], [1, 10], [1, 6], [2, 5], [3, 2]]),
    freqQuery([[1, 3], [1, 38], [2, 1], [1, 16], [2, 1], [2, 2], [1, 64], [1, 84], [3, 1], [1, 100], [1, 10], [2, 2], [2, 1], [1, 67], [2, 2], [3, 1], [1, 99], [1, 32], [1, 58], [3, 2]]),
);






Instead of the three if-statements you could also use switch:

switch(action) {
    case 1: …
    case 2: …
    case 3: …
}


Small improvements

The bottleneck in this functions is indexOf. An alternative is using .includes():

if (action === 3) {
   result.push(frequencies.includes(value) ? 1 : 0);
}


This is faster for all test cases I ran in Chrome and Node.js. It is sometimes faster but can be slower depending highly on the input in Safari and Firefox.

Here's a test on jsfiddle.

Taking it one step further

So, the bottleneck is still the expensive test in case 3:

frequencies.includes(value)


Let's get rid of this by keeping track of the current frequencies ourselves. The main improvement is, that we finally will only have to check whether a given index evaluates to true:

if (action === 3) {
    result.push(frequencyTracker[value] &gt; 0 ? 1 : 0);
}


We introduce a second array frequencyTracker, which is filled with the amount each frequency occurs. I've used the ternary operator simply to shorten the code.



function freqQuery(queries) {
    const frequencies = [];
    const frequencyTracker = [];
    const result = [];

    for (const query of queries) {
        const action = query[0];
        const value = query[1];
        let index;

        if (action === 1 || action === 2) {
            index = frequencies[value];
            frequencyTracker[index] ? --frequencyTracker[index] : null;
        }

        if (action === 1) {
            typeof frequencies[value] === 'undefined' ? frequencies[value] = 1 : ++frequencies[value];
        }

        if (action === 2 &amp;&amp; frequencies[value]) {
            --frequencies[value];
        }

        if (action === 1 || action === 2) {
            index = frequencies[value];
            frequencyTracker[index] ? ++frequencyTracker[index] : frequencyTracker[index] = 1;
        }

        if (action === 3) {
            result.push(frequencyTracker[value] &gt; 0 ? 1 : 0);
        }
    }

    return result;
}

console.log(
    freqQuery([[1,1],[2,2],[3,2],[1,1],[1,1],[2,1],[3,2]]),
    freqQuery([[3, 4], [2, 1003], [1, 16], [3, 1]]),
    freqQuery([[1, 3], [2, 3], [3, 2], [1, 4], [1, 5], [1, 5], [1, 4], [3, 2], [2, 4], [3, 2]]),
    freqQuery([[1, 5], [1, 6], [3, 2], [1, 10], [1, 10], [1, 6], [2, 5], [3, 2]]),
    freqQuery([[1, 3], [1, 38], [2, 1], [1, 16], [2, 1], [2, 2], [1, 64], [1, 84], [3, 1], [1, 100], [1, 10], [2, 2], [2, 1], [1, 67], [2, 2], [3, 1], [1, 99], [1, 32], [1, 58], [3, 2]]),
);




This doesn't look as elegant as the naive solution, but it's way faster. It passes all test cases on HackerRank as well.

Explanation

As requested in the comments, here's a an example to explain how this works in detail:

Before we begin, we create an empty array, which we will become a sparse array later.

Now we run through all queries:

If the action is 1 we set or increase our counter at the given index.

[1, 2] → [undefined, undefined, 1] + [undefined, 1]
[1, 4] → [undefined, undefined, 1, undefined, 1] + [undefined, 2]
[1, 4] → [undefined, undefined, 1, undefined, 2] + [undefined, 1, 1]


If the action is 2 we decrease our counter at the given index.

[2, 2] → [undefined, undefined, 0, undefined, 2] + [1, 0, 1]


If the action is 3 we check whether the value in our frequencyTracker is "true" or "not 0" or "nor undefined".

[3, 1] → [1, 0, 1] → 0
[3, 2] → [1, 0, 1] → 1


Naming

Some variable names are good, others are ambiguous, like:


charToObj()
obj



This should be improved.