Trade memory for lookups. A scan that would otherwise need a second loop to ask "does this value exist anywhere else?" gets its answer in O(1) by remembering what it has already walked past - a set when only membership matters, a map when the position or count matters too.
The shape is almost always the same: one pass, one question per element, and the structure is updated as you go so that every answer it gives is about elements you have genuinely already seen.
Sometimes the question is not "have I seen this value?" but "have I seen anything equivalent to this value?" Reduce each element to a canonical form - a form two equivalent elements always share - and use that as the key. The map then groups by equivalence without ever comparing two elements to each other.
One value can be a member of several groups at once, and has to be unique inside every one of them. Keep a separate set per group and derive each group's index from the element's position, so a single pass asks every question it needs to about a cell before committing it.
When the input announces work by value but the question is asked about position, one prologue pass over the grid inverts it - every value becomes the coordinates of the single cell it names. The grid is then never read again, and the live pass is pure counting against that map.