Replace consecutive duplicate values from array - javascript

In an array of numbers, I need to find repeating values and replace them with null.
Examples
Replace 6 in the middle of array if its neighbors are also 6
[1, 4, 3, 6, 6, 6, 6, 3, 2] => [1, 4, 3, 6, null, null, 6, 3, 2]
Replace 6 at the end of the array if the penultimate value is 6 :
[2, 6, 6, 6, 5, 2, 6, 6] => [2, 6, null, 6, 5, 2, 6, null]
Replace 6 at the start of the array if the next value is 6
[6, 6, 2, 3, 5, 6] => [null, 6, 2, 3, 5, 6]
Any ideas how to achieve this? I'm open to using lodash / underscore if needed

You have to iterate through the array and check for the cases you mentioned.
Check if first element is equal to second element
Check if last element is equal to penultimate element
Check if element is equal to either neighboring element
The following code should achieve what you asked for.
function replaceRepeats(arr) {
for (let i = 0; i < arr.length; i++) {
if (i === 0) {
if (arr[i] === arr[i + 1]) {
arr[i] = null;
}
} else if (i === arr.length - 1) {
if (arr[i] === arr[i - 1]) {
arr[i] = null;
}
} else {
if (arr[i] === arr[i - 1] || arr[i] === arr[i + 1]) {
arr[i] = null;
}
}
}
return arr;
}

You could use map so that you have access to the original array while replacing:
const maskDupes = arr =>
arr.map((val, i, arr) =>
(arr[i-1]??val) == val && (arr[i+1]??val) == val ? null : val
);
console.log(...maskDupes([1, 4, 3, 6, 6, 6, 6, 3, 2]));
console.log(...maskDupes([2, 6, 6, 6, 5, 2, 6, 6]));
console.log(...maskDupes([6, 6, 2, 3, 5, 6]));
Or if the array should be mutated, you could keep track of the current value that must be compared, so you still have it available even when in the array it was replaced by a null:
const maskDupes = arr => {
for (let i = 0, prev = arr[0]; i < arr.length; i++) {
if (prev == arr[i] && arr[i] == (arr[i+1]??prev)) arr[i] = null;
else prev = arr[i];
}
}
let arr = [1, 4, 3, 6, 6, 6, 6, 3, 2];
maskDupes(arr);
console.log(...arr);
arr = [2, 6, 6, 6, 5, 2, 6, 6];
maskDupes(arr);
console.log(...arr);
arr = [6, 6, 2, 3, 5, 6];
maskDupes(arr);
console.log(...arr);

Related

Find position of Paired Numbers in Array

let input arr=[9,4,4,8,90,4,9,4,4,4,4,4,4,4,4,4,7,9,2,4,4,4,4,4,8,4,4,4,4];
let output arr=[1,7,9,11,13,15,19,21,25,27];
Above is an input array of numbers which contain mostly 4, if there is a pair of 4 which means (Input elements has 2 of the number 4 consecutively), its array position will be displayed in the output array. I have tried my code below but I am still unsure on how to solve this :). May I know how to solve this?
console.clear();
let arr=[8,4,4,4,4,4,4,4,4,4,7,9,2,4,4,4,4,4,8,4,4,4,4];
console.log("his")
for (let i=0;i<arr.length;i++){
if (arr[i]!==arr[i+1] &&arr[i]!==4 ){
console.log(i)
}
if (arr[i]!==arr[i+1] &&arr[i+1]!==4 ){
console.log(i+1)
}
}
This is a possible solution:
console.clear();
let arr=[8,4,4,4,4,4,4,4,4,4,7,9,2,4,4,4,4,4,8,4,4,4,4];
let pair = false;
for (let i=0;i<arr.length;i++){
if (pair == false) {
if (arr[i]==arr[i+1]){
console.log(i)
pair = true; // show that we have found a pair
} // thus we skip a 'for' loop
}
else {
pair = false; // reset the pair variable
}
}
output: [1, 3, 5, 7, 13, 15, 19, 21]
Do you want this pair:
let arr=[8,4,4,4,4,4,4,4,4,4,7,9,2,4,4,4,4,4,8,4,4,4,4];
to be counted, as well?
You could look to the next item and if the last index is not in the result array, the add the actual index to the result.
const
input = [9, 4, 4, 8, 90, 4, 9, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 9, 2, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4],
result = input.reduce((r, v, i, a) => {
if (v === a[i + 1] && r[r.length - 1] !== i - 1) r.push(i);
return r;
}, []);
console.log(...result);
An approach for n elements with a closure over a counting variable c.
const
getIndices = (array, n) => input.reduce((c => (r, v, i, a) => {
if (!c) {
if (v === a[i + 1]) c = 1;
return r;
}
c = v === a[i - 1] ? c + 1 : 0;
if (c === n) {
r.push(i - n + 1);
c = 0;
}
return r;
})(0), []),
input = [9, 4, 4, 8, 90, 4, 9, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 9, 2, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4];
console.log(...getIndices(input, 2));
console.log(...getIndices(input, 3));
console.log(...getIndices(input, 4));
console.log(...getIndices(input, 5));

Loops & Control Flow

When I run this I can't seem to get the rest of the values.
Write a function mergingTripletsAndQuints which takes in two arrays as arguments. This function will return a new array replacing the elements in array1 if they are divisible by 3 or 5. The number should be replaced with the sum of itself added to the element at the corresponding index in array2.
function mergingTripletsAndQuints(array1, array2) {
let result = [];
let ctr = 0;
let x = 0;
for (let i = 0; i < array1.length; i++) {
for (let j = 0; j < array2.length; j++) {
ctr = array1[i] + array2[j];
if (ctr % 3 === 0 || ctr % 5 === 0) {
result.push(ctr);
} else {
return array1[i];
}
}
}
return result;
}
console.log(mergingTripletsAndQuints([1, 2, 3, 4, 5, 15], [1, 3, 6, 7, 8, 9])); // expected log [1, 2, 9, 4, 13, 24]
console.log(mergingTripletsAndQuints([1, 1, 3, 9, 5, 15], [1, 2, 3, 4, 5, 6])); // expected log [1, 1, 6, 13, 10, 21]
It is only logging [1], [1]
I'm not sure, but I suppose there is a typo returning array1[i] in nested loop. I suppose you mean result.push(array1[i]) instead.
I think it should be something like this:
function mergingTripletsAndQuints(array1, array2) {
let result = [];
for (let i = 0; i < array1.length; i++) {
if (array1[i]% 3 === 0 || array1[i]% 5 === 0) {
result.push(array1[i] + array2[i]);
} else {
result.push(array1[i]);
}
}
return result;
}
console.log(mergingTripletsAndQuints([1, 2, 3, 4, 5, 15], [1, 3, 6, 7, 8, 9])); // expected log [1, 2, 9, 4, 13, 24]
console.log(mergingTripletsAndQuints([1, 1, 3, 9, 5, 15], [1, 2, 3, 4, 5, 6])); // expected log [1, 1, 6, 13, 10, 21]
A nested for loop is not necessary, look at this code:
function mergingTripletsAndQuints(array1, array2) {
let sum = [];
for (let i = 0; Math.max(i < array1.length, i < array2.length); i++) {
if (array1[i] % 3 == 0 || array1[i] % 5 == 0) {
sum.push(array1[i] + array2[i])
} else {
sum.push(array1[i])
}
}
return sum;
}

Comparing Elements Values In An Array, and returning Boolean

Can anyone help I am nearly there I can get the code to return true for the first 2 test below, but not return false for the next 2 tests, what I need to do is to check that all the numbers in the array are in ascending order so every element number is greater than the last for the length of the array. Any ideas? Kind regards Jon.
Test.expect(inAscOrder([1, 2, 4, 7, 19])
Test.expect(inAscOrder([1, 2, 3, 4, 5])
Test.expect(!inAscOrder([1, 6, 10, 18, 2, 4, 20])
Test.expect(!inAscOrder([9, 8, 7, 6, 5, 4, 3, 2, 1])
function inAscOrder(arr) {
let result = false;
for (let i = 0; i <= arr.length; i++) {
for (let k = 0; k <= arr.length; k++) {
if (arr[i] < arr[k+1]) {
result = true;
}
}
}
return result;
}
You were very close to answer
function inAscOrder(arr) {
let result = true;
for (let i = 0; i <= arr.length; i++) {
if (arr[i] > arr[i+1]) {
result = false;
break;
}
}
return result;
}
Try this,
You can use the functional way instead.
const inAscOrder = arr => arr.slice(1).every((elem,i) => elem > arr[i]);
console.log(inAscOrder([1,2,5]));
The easiest way to compare two arrays, is to convert them to a strings and then compare the strings.
const isAscending = (arr) => arr.join("") == arr.sort((a, b) => a - b).join("");
console.log(isAscending([1, 2, 4, 7, 19]));
console.log(isAscending([1, 2, 3, 4, 5]));
console.log(isAscending([1, 6, 10, 18, 2, 4, 20]));
console.log(isAscending([9, 8, 7, 6, 5, 4, 3, 2, 1]));
For every element in the array, return true if it's the first element or it's greater than the previous element.
const isAscending = (arr) => arr.every((el, i, arr) => i === 0 || el > arr[i - 1]);
console.log(isAscending([1, 2, 4, 7, 19]));
console.log(isAscending([1, 2, 3, 4, 5]));
console.log(isAscending([1, 6, 10, 18, 2, 4, 20]));
console.log(!isAscending([9, 8, 7, 6, 5, 4, 3, 2, 1]));

Count repeated numbers in array and return true (Cognitive Complexity)

I need to check if a number repeats itself at least three times in an array. How can I refactor it to decrease the Cognitive Complexity that Lint keeps complaining about.
Heres my code:
let array11 = [1, 3, 2, 3, 5, 6, 7, 8, 9, 0, 1];
function checkDuplicateNumber (array11) {
for (let i = 0; i < array11.length; i += 1) {
let sameNumberLoop = 0;
for (let i2 = i; i2 < array11.length; i2 += 1) {
if (array11[i] === array11[i2]) {
sameNumberLoop += 1;
if (sameNumberLoop >= 3) {
return true;
}
}
}
}
}
Instead of iterating multiple times, iterate just once, while counting up the number of occurrences in an object or Map:
let array11 = [1, 3, 2, 3, 5, 6, 7, 8, 9, 0, 1];
function checkDuplicateNumber (array) {
const counts = {};
for (const num of array) {
counts[num] = (counts[num] || 0) + 1;
if (counts[num] === 3) return true;
}
return false;
};
console.log(checkDuplicateNumber(array11));
console.log(checkDuplicateNumber([3, 1, 3, 5, 3]));
let array11 = [1, 3, 2, 3, 5, 6, 7, 8, 9, 0, 1]
let array22 = [1, 3, 2, 3, 5, 6, 7, 1, 9, 0, 1]
function checkDuplicateNumber(arr) {
const map = new Map()
return arr.some((v) => (map.has(v) ? (++map.get(v).count === 3) : (map.set(v, { count: 1 }), false)))
}
console.log(checkDuplicateNumber(array11))
console.log(checkDuplicateNumber(array22))

Get n non overlapping m-sized samples from an array

Given an array, how can I extract n non overlapping random samples of size m from it?
For example, given the array:
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
calling sample(arr, 3, 2) would for example return [[7, 8], [4, 5], [2, 3]], calling sample(arr, 2, 4) would necessarily return [[1, 2, 3, 4], [5, 6, 7, 8], and calling sample(arr, 5, 2) would throw an error.
EDIT - Maybe this wasn't clear in the initial question: samples should be lists of contiguous elements. That is why sample(arr, 2, 4) can only return [[1, 2, 3, 4], [5, 6, 7, 8] and not [[2, 3, 1, 6], [5, 4, 7, 8], for example.
You could start off by first creating a list with the format of the return value:
[ 1, 2, 3, 4, 5, 6, 7, 8]
[<---->, <---->, <---->, <>, <>] // sample(array, 3, 2)
[<------------>, <------------>] // sample(array, 2, 4)
These format arrays could be written out using the lengths:
[1, 2, 3, 4, 5, 6, 7, 8]
[ 2, 2, 2, 1, 1] // sample(array, 3, 2)
[ 4, 4] // sample(array, 2, 4)
Then shuffle the format arrays to gain a random sample selection:
[1, 2, 3, 4, 5, 6, 7, 8]
[ 2, 1, 2, 2, 1] // sample(array, 3, 2)
[ 4, 4] // sample(array, 2, 4)
Then for each element of the format array, remove the the first n elements from the input array. Then store them unless it was a filler (one size chunks that are put in to reach the array length).
[1, 2, 3, 4, 5, 6, 7, 8]
[[1,2], [4,5], [6,7]] // sample(array, 3, 2)
[[1,2,3,4], [5,6,7,8]] // sample(array, 2, 4)
Lastly shuffle the resulting samples.
[1, 2, 3, 4, 5, 6, 7, 8]
[[4,5], [1,2], [6,7]] // sample(array, 3, 2)
[[5,6,7,8], [1,2,3,4]] // sample(array, 2, 4)
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
console.log(sample(arr, 3, 2));
console.log(sample(arr, 2, 4));
console.log(sample(arr, 5, 2));
function randomInt(limit) {
return Math.floor(Math.random() * limit);
}
function shuffle(array) {
for (let limit = array.length; limit > 0; --limit)
array.push(...array.splice(randomInt(limit), 1));
}
function sample(array, sampleCount, sampleLength) {
let elementCount = sampleCount * sampleLength;
if (elementCount > array.length)
throw "invalid sampleCount/sampleLength arguments";
const filler = {valueOf: () => 1};
const fillerCount = array.length - elementCount;
const lengths = Array.from(
{length: sampleCount + fillerCount},
(_, i) => i < sampleCount ? sampleLength : filler
);
shuffle(lengths);
const samples = Array.from(array);
for (const length of lengths) {
const sample = samples.splice(0, length);
if (length === filler) continue;
samples.push(sample);
}
shuffle(samples);
return samples;
}
Note that === is important in length === filler. If you use ==, filler would also equal 1. This would then conflict with a call like sample(array, 5, 1) where each sample length is 1.
const filler = {valueOf: () => 1};
console.log("1 == filler //=>", 1 == filler);
console.log("2 == filler //=>", 2 == filler);
console.log("filler == filler //=>", filler == filler);
console.log("1 === filler //=>", 1 === filler);
console.log("2 === filler //=>", 2 === filler);
console.log("filler === filler //=>", filler == filler);
you can use a greedy algorithm, and take m-sized n tuples from the shuffled array:
const arr = [2, 1, 3, 4, 5, 6, 7, 8];
function sample(arr, length, size){
if(arr.length < length*size)
throw new Error("too short");
arr.sort(() => Math.random() - 0.5);
let res = [];
for(let i = 0; i < length; i++) res.push(arr.slice(i*size, i*size+size));
return res;
}
console.log(sample(arr, 2, 4));
I think the best implementation would shuffle first. Here's my two cents:
function shuffle(array){
let a = array.slice(), i = a.length, n, h;
while(i){
n = Math.floor(Math.random()*i--); h = a[i]; a[i] = a[n]; a[n] = h;
}
return a;
}
function sample(array, chunks, count){
const r = [], a = shuffle(array);
for(let n=0; n<chunks; n++){
r.push(a.splice(0, count));
}
return r;
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
console.log(sample(arr, 3, 2)); console.log(sample(arr, 2, 4));
You can do this with Rando.js (which is cryptographically secure), map, and splice pretty easily. Just use randojs' randoSequence function to shuffle the provided array and splice n size-m arrays out of that shuffled array to get everything we need to return. If the provided array has too few values, the later arrays that we return will just be shorter.
function sample(arr, n, m){
arr = randoSequence(arr).map(i => i.value), sample = [];
for(var i = 0; i < n; i++) sample[i] = arr.splice(-m);
return sample;
}
console.log(sample([1, 2, 3, 4, 5, 6, 7, 8], 3, 2));
<script src="https://randojs.com/2.0.0.js"></script>

Categories

Resources