/now
projects
ramblings
smol projects

delete occurrences of extra elements in an array

18.11.2023 2 min read

Create a function that takes two arguments: an array arr and a number num. If an element occurs in arr more than num times, remove the extra occurrence(s) and return the result.

deleteOccurrences([1, 1, 1, 1], 2) ➞ [1, 1]
deleteOccurrences([13, true, 13, null], 1) ➞ [13, true, null]
deleteOccurrences([true, true, true], 3) ➞ [true, true, true]

Two solutions:

  1. Initialize a new array. For each element, we check if the array already contains i number of elements. If it does, we do nothing. If not, we append it to the array.
  2. Deduplicate the original array, map over it and somehow find a way to repeat the elements i times, flattening it when we’re done.

Crystal:

def delete_occurrences(arr : Array(T), i : Int32) forall T
  res = [] of T
  arr.each { |el| res << el if res.count(el) < i }
  res
end

def delete_occurrences_func(arr : Array(T), i : Int32) forall T
  arr.uniq.flat_map { |el| Array.new(i, el) }
end

Nim:

func deleteOccurrences[T](s: seq[T], i: int): seq[T] =
    for el in s:
        if result.count(el) == i: continue
        result.add(el)

func deleteOccurrencesFunc[T](s: seq[T], i: int): seq[T] =
    let newS = collect(newSeq):
        for el in s.deduplicate:
            el.repeat(i)
    return newS.foldl(a & b)

Found another use for collect, which I’ve been using a lot more in recent days.

Raku:

sub delete-occurrences(@a, $i) {
    my @n = [];
    for @a -> $el {
        @n.push($el) if @n.grep({$_ eqv $el}).elems < $i;
    }
    @n;
}

sub delete-occurrences-func(@a, $i) {
    (do for @a.unique {$_ xx $i}.flat).Array;
}

ok delete-occurrences([13, True, 13, True], 1) == [13, True];

A gotcha: we cannot use smartmatching for the first solution when grepping! Any value that is truthy will evaluate to true, and that means we get a lot more elements than we want! Instead, we check that $_ is strictly eqv to $el.

Javascript:

function deleteOccurrences<T>(a: T[], i: number) {
  const newA = [];
  const map: Map<T, number> = new Map();

  for (const el of a) {
    const count = map.get(el) || 0;
    if (count !== i) {
      map.set(el, count + 1);
      newA.push(el);
    }
  }

  return newA;
}

function deleteOccurrencesFunc<T>(a: T[], i: number) {
  const newA = [...new Set(a)];
  return newA.flatMap((el) => new Array(i).fill(el));
}

Ugh… that first implementation is a lot longer than I would like. I don’t get why we don’t have count yet in Javascript.

Built with Astro and Tailwind 🚀