1

I'm trying to filter the given array to a new array with ONLY repeating numbers:

const array = [1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 1, 1];

I tried this code:

const findDuplicates = (arr) => arr.filter((item, index) => arr.indexOf(item) !== index);

But the output that I'm getting is this:

[1, 1, 7, 8, 2, 2, 2, 2, 2, 2, 2, 1, 1]

As you can see that in this array there are missing numbers. It should be like this:

[1, 1, 1, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1]

What I'm doing wrong?

2
  • 2
    Will the duplicates always be in sequence? Commented Apr 24, 2022 at 20:11
  • No, but I'd like to have a solution for that too. Commented Apr 24, 2022 at 20:45

3 Answers 3

1

You could check the value with value at previous index or next index.

const
    array = [1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 1, 1],
    result = array.filter((value, i, { [i - 1]: prev, [i + 1]: next }) =>
        prev === value || value === next
    );

console.log(...result);

Sign up to request clarification or add additional context in comments.

6 Comments

@ArnasOFC ... look at Ninas result (which is reasonable) and compare with what the requirements say ... It's not quit clear yet what the OP really wants as result, for the OP e.g. presents as expected result 8 times the number value 2 (whereas with Nina's approach one receives 7 times 2), of cause with the 8th occurrence out of any expected order. Thus, comes the OP's expected result with a typo?
Don't know if it's a possible input, but just as a remark this would fail if the duplicates were not to appear in sequence like e.g in with this input [1, 2, 1, 2].
@Mushroomator then just after the array sort them out array.sort((a, b) => a - b)
Yes, you could sort them beforehand. Then it will work. It won't though if the order must be preserved.
@Nina Scholz Could you explain or give me a doc link on how to understand this part - { [i - 1]: prev, [i + 1]: next } in the filter parameters. I didn't know it was possible to add an object in filter parameters.
|
0

This solution is a little more manual.

Basically, for each element, see if the next one matches. Then continue checking until you get a non-match. As long as you're getting matches, you push those onto the result array.

const inputArray = [1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 1, 1];

const findDuplicates = (arr) => {
  const result = [];
  const nn = arr.length;
  let ii = 0;

  while (ii < nn) {
    current = arr[ii];
    next = arr[ii+1]
    let duplicate = false
    while (current == next) {
      duplicate = true
      result.push(current);
      ++ii;
      next = arr[ii+1]
    }
    if (duplicate) {
      result.push(current)
    }
    ++ii;
  }
  return result
};

console.log(findDuplicates(inputArray))

Comments

0

This solution also works if the duplicates do not appear in a sequence like in your example. Take for instance the following array:

[1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 2, 3, 2, 3, 2, 3, 2, 1, 1]

Here there are more than one 2s but they do not all occur one after another. In such a case the other proposed solutions would not work as they only take the previous value into account. As you've indicated in your comment you would additionally like to have a solution for such cases here my two solutions.

One solution returns the array with the elements in the same order they were in the original array, the other just returns all identical elements in sequence. As a JavaScript Map is used, which preserves the insertion order, the output order will be determined by the first occurrence of a value in the original array in that case.

Solution which preserves order

/**
 * Counts values in array.
 * @param {any[]} array 
 * @returns 
 */
function valueCounts(array) {
  return array.reduce(
    (valCounts, cur) => valCounts.set(cur, (valCounts.get(cur) || 0) + 1),
    new Map()
  );
}

function duplicatesWithOrder(array) {
  // count occurrences of each item
  const valCounts = valueCounts(array);
  // only add items to final array which have occurred more than once
  return array.filter((item) => valCounts.get(item) > 1);
}

const yourExample = [1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 1, 1];
const withoutSequence = [1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 2, 3, 2, 3, 2, 3, 2, 1, 1];

console.log("With order");
console.log(duplicatesWithOrder(yourExample));
console.log(duplicatesWithOrder(withoutSequence));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Solution which ignores the order

/**
 * Generator which repeats a "value" "times" times.
 * @param {any} value value to repeat
 * @param {number} times number of times to repeat the value
 */
function* repeat(value, times) {
  for (let index = 0; index < times; index++) {
    yield value;
  }
}

/**
 * Counts values in array.
 * @param {any[]} array 
 * @returns 
 */
function valueCounts(array) {
  return array.reduce(
    (valCounts, cur) => valCounts.set(cur, (valCounts.get(cur) || 0) + 1),
    new Map()
  );
}

function duplicatesIgnoreOrder(array) {
  // count occurrences of each item
  const encountered = valueCounts(array);
  // only add items to final array which have occurred more than once
  return [...encountered.entries()].reduce(
    (all, [item, occurrences]) => (
      occurrences > 1 && all.push(...repeat(item, occurrences)), all
    ),
    []
  );
}

const yourExample = [1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 1, 1];
const withoutSequence = [1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 2, 3, 2, 3, 2, 3, 2, 1, 1];

console.log("Ignore order");
console.log(duplicatesIgnoreOrder(yourExample));
console.log(duplicatesIgnoreOrder(withoutSequence));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.