I have a scatter chart (using apexchart) and I am trying to prepare my data (objects in an array) before adding it to the chart.

My problem is that I often have some data that can have the exact same values on the x and y axis. This means that they will overlap eachother and only one item will be shown.

I first added a simple solution by using Math.random() to each duplicated value, but this means that they will be repositioning slightly for each time the chart is updated, which will make the users confused.

So what I want to achieve is to add the same “jitter”/changes for each items all the time. So my thought is to do it like this:

//1. The first three duplicates should add 0.05 to the x value. So it will say: item1.x = 10 //original value item2.x = 10.05 item3.x = 10.1 item4.x = 10.15 //2. Then, If more duplicates than 3, the next 3 should reduce 0.05 of the original value: item5.x = 9.95 item6.x = 9.90 item7.x = 9.85 //If more than 7 items with the same value, then just use the original value.

How could be the best way to achieve this? Right now I have the duplicated values and I can add “jitter”, but how can I make it so it keeps count and do as I want above?

I managed to filter out the duplicates, but then I got stuck:

const duplicates = AllItems.filter((val, i, self) => self.findIndex(item => item.x == val.x && item.y == val.y) != i);

Any ideas?

## Answer

Consider using `Map`

built-in. It performs better in this case, since there are frequent additions and removals of key-value pairs.

This said, it’s worth mentioning it acts like a hash table implementation. The idea is to keep track of the number of times a given `x`

occurs by adding one to its `count`

each time you find one occurrence. For the `points`

example in the snippet, consider the following Map table:

╔═══════╦═══════════════════════════════╗ ║ x ║ 1 2 3 4 5 6 7 8 9 10 ║ ╠═══════╬═══════════════════════════════╣ ║ count ║ 5 1 1 1 7 1 1 1 1 10 ║ ╚═══════╩═══════════════════════════════╝

Note that the `y`

value of points whose `x`

is `1`

won’t change because its count is 5. Whereas `5`

and `10`

will, due to their count being greater or equal than **MIN_TIMES** (7). The logic follows:

- Construct the Map in order to store how many times a given
`x`

point repeats its`y`

coordinate. - Store each
`x`

only those that happens more or equal than`MIN_TIMES`

in array - At the same time, change count to 0 to start counting
- Map each point in the
`points`

array and convert those who are in`xRepeatedAtLeastMinTimes`

to increment or decrement, based on your logic.

const points=[{x:1,y:1},{x:1,y:1},{x:1,y:1},{x:1,y:1},{x:1,y:1},{x:2,y:2},{x:3,y:3},{x:4,y:4},{x:5,y:5},{x:5,y:5},{x:5,y:5},{x:5,y:5},{x:5,y:5},{x:5,y:5},{x:5,y:5},{x:6,y:6},{x:7,y:7},{x:8,y:8},{x:9,y:9},{x:10,y:10},{x:10,y:10},{x:10,y:10},{x:10,y:10},{x:10,y:10},{x:10,y:10},{x:10,y:10},{x:10,y:10},{x:10,y:10},{x:10,y:10}]; const m = new Map(), xRepeatedAtLeastMinTimes = new Array(), INTERVAL = 0.05, MIN_TIMES = 7 // Construct map points.forEach(point => m.set(point.x, m.has(point.x) ? (m.get(point.x) + 1) : 1)) // Get x coordinates that happens at least MIN_TIMES and change its count to 0 for (let [x, count] of m.entries()) count >= MIN_TIMES && xRepeatedAtLeastMinTimes.push(x), m.set(x, 0) // Map each point and check condition based on count stored on Map let result = points.map(point => { let x = point.x if (xRepeatedAtLeastMinTimes.includes(x)) { let count = m.get(x) m.set(x, count + 1) return count === 0 ? point : count <= 3 ? { ...point, x: x + count*INTERVAL } : count <= 6 ? { ...point, x: x - (count - 3)*INTERVAL } : point } return point }) console.log(result)