How to reduce JS array length if next elements equal previous - javascript

I'd like to combine identical elements in an array, into a single term with how many times the value appears
function combineArrayElements(arr) {
return arr.map((e, i, ar) => {
if (e === ar[i + 1] || (e[0] && e[0] === ar[i + 1])) {
return [e, e[1] + 1]
}
return e;
})
}
Some example input and output:
// input [3, 2, 2, 5, 1, 1, 7, 1]
// output [3,[2,2],5,[1,2],7,1]
// input [1, 1, 1, 2, 1]
// output [[1,3], 2, 1]

You could reduce the array and if the value is equal the last value, take an array and increment the counter.
const
getGrouped = array => array.reduce((r, v, i, { [i - 1]: last }) => {
if (v === last) {
if (!Array.isArray(r[r.length - 1])) r[r.length - 1] = [r[r.length - 1], 1];
r[r.length - 1][1]++;
} else {
r.push(v);
}
return r;
}, []);
console.log(getGrouped([3, 2, 2, 5, 1, 1, 1, 7, 1]));
console.log(getGrouped([2, 2, 2, 3]));

Thank, Joseph Cho - answer is:
function splitCounts(arr) {
let res = [];
let count = 1;
for (let i=0; i<arr.length; i++) {
if (arr[i] === arr[i+1]) {
count++;
} else {
res.push([arr[i], count]);
count = 1;
}
}
return res;
}
// [[3,1],[2,2],[5,1],[1,2],[7,1],[1,1]]
console.log(splitCounts([3,2,2,5,1,1,7,1]));

Refactored Nina Scholz's answer with longer variable names, comments, and slightly different control of flow:
const combineAdjacentElements = array => array.reduce((acc, val, i) => {
// if cur is different from prev value, add raw number
if (val !== array[i - 1]) {
acc.push(val)
return acc
}
// if first repetition, replace val with grouped array
if (typeof acc.at(-1) === 'number') {
acc[acc.length - 1] = [val, 2];
return acc
}
// if multiple repetition, increment counter in grouped array
acc.at(-1)[1]++;
return acc;
}, []);
const output = combineAdjacentElements([3, 2, 2, 2, 5, 1, 1, 7, 1])
console.log(JSON.stringify(output, null, 0))
// [3,[2,3],5,[1,2],7,1]
Further Reading
Check whether variable is number
How the get the last element in an array
How to replace the last index value in array javascript

Just use Array.reduce()
to get that.

Related

Return a nested array

I need help making this function after taking an array and another array (duplicate) that has just the numbers that are duplicated in the first array (for example array=[1,2,3,1,2,3,4,5,6,6], duplicate=[1,2,3,6]).
I want it to return an array as follow: finalArray1=[[1,1],[2,2],[3,3],4,5,[6,6]].
let input = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
let sortArray = array => {
return array.sort(function(a, b) {
return a - b;});
}
function findDuplicates(data) {
let duplicate = [];
data.forEach(function(element, index) {
// Find if there is a duplicate or not
if (data.indexOf(element, index + 1) > -1) {
// Find if the element is already in the duplicate array or not
if (duplicate.indexOf(element) === -1) {
duplicate.push(element);
}
}
});
return duplicate;
}
let newArray = (array, duplicate) => {
for( var i = 0; i < 3; i++ ){
for( var j = 0; j < 15; j++ ){
if( duplicate[i] == array[j] ){
let finalArray = new array().push(array[j]);
}
}
return finalArray;
}
}
You could take a Map and return in original order.
The map takes the items in insertation order, which means all item are later in original order. If a key exists, it createsd an array with the value. Otherwise just the item form the array is taken as value for the map.
At the end take only the values from the map and create an array of it.
let input = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20],
result = Array.from(input
.reduce((m, v) => m.set(v, m.has(v) ? [].concat(m.get(v), v) : v), new Map)
.values()
);
console.log(result);
A more traditional approach
let input = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20],
result = input
.sort((a, b) => a -b)
.reduce((r, v, i, a) => {
if (a[i - 1] !== v && v !== a[i + 1]) r.push(v); // check if unique
else if (a[i - 1] !== v) r.push([v]); // check left element
else r[r.length - 1].push(v);
return r;
}, []);
console.log(result);
I think the following can work for you:
const input = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
const inOrder = input.sort((a, b) => a - b);
const unique = inOrder.reduce((a, c) => {
const found = a.find(e => e.includes(c));
if (found) found.push(c);
else a.push([c]);
return a;
}, []);
const result = unique.map(e => e.length > 1 ? e : e[0]);
console.log(result);
I hope this helps!

Search for numbers in an array with a certain amount

i have an array for example [10, 5, 6, 3], also a variable b = 13, I wanna calculate the sum of each element of the array, if the sum equal = b(13) returns true, i know first and last elements sum = 13. How can I check which pair of element's sum?
You could take a hash table for the missing parts and return if found.
function check(array, sum) {
var hash = {},
i = 0,
v;
while (i < array.length) {
v = array[i];
if (hash[v]) return true;
hash[sum - v] = true;
++i;
}
return false;
}
console.log(check([10, 5, 6, 3], 13));
console.log(check([10, 5, 6, 3], 17));
You can use this algorithm to check if two values in array have the given sum or not.
function pairWithSum(array,sum){
//sort the array
const arr = array.sort((a,b) => a -b);
while(arr.length > 1){
if(arr[0] + arr[arr.length - 1] > sum) arr.pop()
if(arr[0] + arr[arr.length - 1] < sum) arr.shift()
if(arr[0] + arr[arr.length - 1] === sum) return true
}
return false;
}
console.log(pairWithSum([10,3,5,6,1],1))
console.log(pairWithSum([10,3,5,6,1],11))
console.log(pairWithSum([10,3,5,6,1],13));
console.log(pairWithSum([10,3,5,6,1],55))

Check array in JS - is list sorted? [duplicate]

This question already has answers here:
Check if an array is descending, ascending or not sorted?
(10 answers)
Check if array values are ascending or descending
(1 answer)
Closed 4 years ago.
I need to create a program that checks the list in the array is sorted. I have three input data:
1,2,3,4,5
1,2,8,9,9
1,2,2,3,2
So here is my code:
let sorts = +gets(); // 3
let list = [];
for (let i = 0; i < sorts; i++) {
list[i] = gets().split(',').map(Number); // The Array will be: [ [ 1, 2, 3, 4, 5 ], [ 1, 2, 8, 9, 9 ], [ 1, 2, 2, 3, 2 ] ]
}
for (let i = 0; i < list[i][i].length; i++){
if (list[i][i] < list[i][i +1]) {
print('true');
} else {
print('false');
}
}
I need to print for all lists on new line true or false. For this example my output needs to be:
true
true
false
I have no idea how to resolve this.
You can use array#every to check if each value is greater than the previous value.
const isSorted = arr => arr.every((v,i,a) => !i || a[i-1] <= v);
console.log(isSorted([1,2,3,4,5]));
console.log(isSorted([1,2,8,9,9]));
console.log(isSorted([1,2,2,3,2]));
How about something like this:
!![1,2,3,4,5].reduce((n, item) => n !== false && item >= n && item)
// true
!![1,2,8,9,9].reduce((n, item) => n !== false && item >= n && item)
// true
!![1,2,2,3,2].reduce((n, item) => n !== false && item >= n && item)
// false
Reduce will literally reduce the array down to a single value - a boolean in our case.
Here, we are calling a function per iteration, the (n, item) is our function signature, it's body being n !== false && item >- n && item - we are making sure that n exists (n is our accumulator - read up!), testing if item is greater than n, and making sure item exists.
This happens for every element in your array. We then use !! to force the result into a tru boolean.
Simply try this way by using slice method : It will check if previous element is less than the next element.If the condition is true for every element then it will return true else false
arr.slice(1).every((item, i) => arr[i] <= item);
Checkout this below sample as Demo
var arr = [[1,2,3,4,5],[1,2,8,9,9],[1,2,2,3,2],[0,1,2,3,4,5]];
function isArrayIsSorted (arr) {
return arr.slice(1).every((item, i) => arr[i] <= item)
}
var result= [];
for (var i = 0; i < arr.length; i++){
result.push(isArrayIsSorted(arr[i]))
}
console.log(result);
Sorted Number Lists
Including Negative Numbers, Zeros, and Adjacent Duplicates
Use every() method which will return true should all of the numbers be in order otherwise it will return false. The conditions are as follows:
(num <= arr[idx + 1]) || (idx === arr.length - 1)
if the current number is less than or equal to the next number...
OR...
if the current index is equal to the last index...
return 1 (truthy)
Demo
var arr0 = [1, 2, 3, 4, 5];
var arr1 = [1, 2, 8, 9, 9];
var arr2 = [1, 2, 2, 3, 2];
var arr3 = [0, 0, 0, 1, 3];
var arr4 = [-3, 0, 1, 3, 3];
var arr5 = [-4, -2, 0, 0, -4];
function sorted(array) {
return array.every(function(num, idx, arr) {
return (num <= arr[idx + 1]) || (idx === arr.length - 1) ? 1 : 0;
});
}
console.log(arr0 +' | '+sorted(arr0));
console.log(arr1 +' | '+sorted(arr1));
console.log(arr2 +' | '+sorted(arr2));
console.log(arr3 +' | '+sorted(arr3));
console.log(arr4 +' | '+sorted(arr4));
console.log(arr5 +' | '+sorted(arr5));
var str = ["1,2,3,4,5", "1,2,8,9,9", "1,2,2,3,2"];
for (var i in str){
var list = str[i].split(',').map(Number);
console.log(list);
var isSorted = true;
for(var j = 0 ; j < list.length - 1 ; j++){
if(list[j] > list[j+1]) {
isSorted = false;
break;
}
}
console.log(isSorted);
}
Maybe you can use this helping method that checks if is sorted correctly:
var arr1 = [1, 2, 3, 4, 4];
var arr2 = [3, 2, 1];
console.log(checkList(arr1));
console.log(checkList(arr2));
function checkList(arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i + 1]) {
if (arr[i] > arr[i + 1]) {
return false;
}
}
}
return true;
}
There are plenty of ways how to do that. Here is mine
const isArraySorted = array =>
array
.slice(0) // clone array
.sort((a, b) => a - b) // sort it
.every((el, i) => el === array[i]) // compare with initial value)
You can check if stringified sorted copy of original array has same value as the original one. Might not be the most cool or performant one, but I like it's simplicity and clarity.
const arraysToCheck = [
[1, 2, 3, 4, 5],
[1, 2, 8, 9, 9],
[1, 2, 2, 3, 2]
]
const isSorted = arraysToCheck.map(
item => JSON.stringify([...item].sort((a, b) => a - b)) === JSON.stringify(item)
);
console.log(isSorted);
If i get what you mean, you want to know if an array is sorted or not. This is an example of such a solution, try it. I pasted some codes below.
var myArray=[1,4,3,6];
if(isSorted(myArray)){
console.log("List is sorted");
}else{
console.log("List is not sorted");
}
function isSorted(X){
var sorted=false;
for(var i=0;i<X.length;i++){
var next=i+1;
if (next<=X.length-1){
if(X[i]>X[next]){
sorted=false;
break;
}else{
sorted=true;
}
}
}
return sorted;
}

Remove smallest number in JS Array

I have a number array [2, 1, 3, 4, 5, 1] and want to remove the smallest number in the list. But somehow my IF statement gets skipped.
I checked and by itself "numbers[i + 1]" and "numbers[i]" do work, but "numbers[i + 1] < numbers[i]" doesn't...
function removeSmallest(numbers) {
var smallestNumberKEY = 0;
for (i = 0; i <= numbers.lenths; i++) {
if (numbers[i + 1] < numbers[i]) {
smallestNumberKEY = i + 1;
}
}
numbers.splice(smallestNumberKEY, 1);
return numbers;
}
document.write(removeSmallest([2, 1, 3, 4, 5, 1]));
You have a typo in your code, array doesn't have lenths property
function removeSmallest(numbers) {
var smallestNumberKEY = 0;
for (var i = 0; i < numbers.length - 1; i++) {
if (numbers[i + 1] < numbers[i]) {
smallestNumberKEY = i + 1;
numbers.splice(smallestNumberKEY, 1);
}
}
return numbers;
}
document.write(removeSmallest([2, 1, 3, 4, 5, 1]));
But your algorithm wont work for another array, e.g [5, 3, 1, 4, 1], it will remove a value 3 too.
You can find the min value with Math.min function and then filter an array
function removeSmallest(arr) {
var min = Math.min(...arr);
return arr.filter(e => e != min);
}
You can use Array#filter instead
function removeSmallest(arr) {
var min = Math.min.apply(null, arr);
return arr.filter((e) => {return e != min});
}
console.log(removeSmallest([2, 1, 3, 4, 5, 1]))
Short one liner. If the smallest value exist multiple times it will only remove ONE. This may or may not be what you want.
const result = [6,1,3,1].sort().filter((_,i) => i) // result = [1,3,6]
It works by sorting and then creating a new array from the items where indeces are truthy(anything but 0)
another solution with splice and indexOf:
array = [2, 1, 3, 4, 5, 1];
function replace(arr){
arr = arr.slice(); //copy the array
arr.splice( arr.indexOf(Math.min.apply(null, arr)),1)
return arr;
}
document.write( replace(array) ,'<br> original array : ', array)
edit : making a copy of the array will avoid the original array from being modified
"Short" solution using Array.forEach and Array.splice methods:
function removeSmallest(numbers) {
var min = Math.min.apply(null, numbers);
numbers.forEach((v, k, arr) => v !== min || arr.splice(k,1));
return numbers;
}
console.log(removeSmallest([2, 1, 3, 4, 5, 1])); // [2, 3, 4, 5]
This is a proposal with a single loop of Array#reduce and without Math.min.
The algorithm sets in the first loop min with the value of the element and returns an empty array, because the actual element is the smallest value and the result set should not contain the smallest value.
The next loop can have
a value smaller than min, then assign a to min and return a copy of the original array until the previous element, because a new minimum is found and all other previous elements are greater than the actual value and belongs to the result array.
a value greater then min, then the actual value is pushed to the result set.
a value equal to min, then the vaue is skipped.
'use strict';
var removeSmallest = function () {
var min;
return function (r, a, i, aa) {
if (!i || a < min) {
min = a;
return aa.slice(0, i);
}
if (a > min) {
r.push(a);
}
return r;
}
}();
document.write('<pre>' + JSON.stringify([2, 1, 3, 2, 4, 5, 1].reduce(removeSmallest, []), 0, 4) + '</pre>');
I like this oneliner: list.filter(function(n) { return n != Math.min.apply( Math, list ) })
check it out here: https://jsfiddle.net/rz2n4rsd/1/
function remove_smallest(list) {
return list.filter(function(n) { return n != Math.min.apply( Math, list ) })
}
var list = [2, 1, 0, 4, 5, 1]
console.log(list) // [2, 1, 0, 4, 5, 1]
list = remove_smallest(list)
console.log(list) // [2, 1, 4, 5, 1]
list = remove_smallest(list)
console.log(list) // [2, 4, 5]
I had to do this but I needed a solution that did not mutate the input array numbers and ran in O(n) time. If that's what you're looking for, try this one:
const removeSmallest = (numbers) => {
const minValIndex = numbers.reduce((finalIndex, currentVal, currentIndex, array) => {
return array[currentIndex] <= array[finalIndex] ? currentIndex : finalIndex
}, 0)
return numbers.slice(0, minValIndex).concat(numbers.slice(minValIndex + 1))
}
function sumOfPaiars(ints){
var array = [];
var min = Math.min(...ints)
console.log(min)
for(var i=0;i<ints.length;i++){
if(ints[i]>min){
array.push(ints[i])
}
}
return array
}
If you only wish to remove a single instance of the smallest value (which was my use-case, not clear from the op).
arr.sort().shift()
Here is a piece of code that is work properly but is not accepted from codewars:
let numbers = [5, 3, 2, 1, 4];
numbers.sort(function numbers(a, b) {
return a - b;
});
const firstElement = numbers.shift();

Finding the local maxima in a 1D array

Is there an easy way to find the local maxima in a 1D array?
Let's say I have an array:
[ 0,
1,
10, <- max
8, <- (ignore)
3,
0,
0,
4,
6, <- (ignore)
10, <- max
6, <- (ignore)
1,
0,
0,
1,
4, <- max
1,
0 ]
I want it to find the 10s and the 4, but ignore the 8 & 6, since those are next to 10s. Mathematically, you could just find where derivative is equal to zero if it were a function. I'm not too sure how to do this in Javascript.
This will return an array of all peaks (local maxima) in the given array of integers, taking care of the plateaus as well:
function findPeaks(arr) {
var peak;
return arr.reduce(function(peaks, val, i) {
if (arr[i+1] > arr[i]) {
peak = arr[i+1];
} else if ((arr[i+1] < arr[i]) && (typeof peak === 'number')) {
peaks.push(peak);
peak = undefined;
}
return peaks;
}, []);
}
findPeaks([1,3,2,5,3]) // -> [3, 5]
findPeaks([1,3,3,3,2]) // -> [3]
findPeaks([-1,0,0,-1,3]) // -> [0]
findPeaks([5,3,3,3,4]) // -> []
Note that the first and last elements of the array are not considered as peaks, because in the context of a mathematical function we don't know what precedes or follows them and so cannot tell if they are peaks or not.
maxes = []
for (var i = 1; i < a.length - 1; ++i) {
if (a[i-1] < a[i] && a[i] > a[i+1])
maxes.push(a[i])
}
This code finds local extrema (min and max, where first derivation is 0, and ), even if following elements will have equal values (non-unique extrema - ie. '3' is choosen from 1,1,1,3,3,3,2,2,2)
var GoAsc = false; //ascending move
var GoDesc = false; //descending move
var myInputArray = [];
var myExtremalsArray = [];
var firstDiff;
for (index = 0; index < (myArray.length - 1); index++) {
//(myArray.length - 1) is because not to exceed array boundary,
//last array element does not have any follower to test it
firstDiff = ( myArray[index] - myArray[index + 1] );
if ( firstDiff > 0 ) { GoAsc = true; }
if ( firstDiff < 0 ) { GoDesc = true; }
if ( GoAsc === true && GoDesc === true ) {
myExtremalsArray.push(myArray[index]);
GoAsc = false ;
GoDesc = false;
//if firstDiff > 0 ---> max
//if firstDiff < 0 ---> min
}
}
The two situations are when you have a peak, which is a value greater than the previous and greater than the next, and a plateau, which is a value greater than the previous and equal to the next(s) until you find the next different value which must me less.
so when found a plateau, temporary create an array from that position 'till the end, and look for the next different value (the Array.prototype.find method returns the first value that matches the condition ) and make sure it is less than the first plateau value.
this particular function will return the index of the first plateau value.
function pickPeaks(arr){
return arr.reduce( (res, val, i, self) => {
if(
// a peak when the value is greater than the previous and greater than the next
val > self[i - 1] && val > self[i + 1]
||
// a plateau when the value is greater than the previuos and equal to the next and from there the next different value is less
val > self[i - 1] && val === self[i + 1] && self.slice(i).find( item => item !== val ) < val
){
res.pos.push(i);
res.peaks.push(val);
}
return res;
}, { pos:[],peaks:[] } );
}
console.log(pickPeaks([3,2,3,6,4,1,2,3,2,1,2,3])) //{pos:[3,7],peaks:[6,3]}
console.log(pickPeaks([-1, 0, -1])) //{pos:[1],peaks:[0]}
console.log(pickPeaks([1, 2, NaN, 3, 1])) //{pos:[],peaks:[]}
console.log(pickPeaks([1, 2, 2, 2, 1])) //{pos: [1], peaks: [2]} (plateau!)
How about a simple iteration?
var indexes = [];
var values = [0,1,10,8,3,0,0,4,6,10,6,1,0,0,1,4,1,0];
for (var i=1; i<values.length-1; i++)
if (values[i] > values[i-1] && values[i] > values[i+1])
indexes.push(i);
more declarative approach:
const values = [3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3];
const findPeaks = arr => arr.filter((el, index) => {
return el > arr[index - 1] && el > arr[index + 1]
});
console.log(findPeaks(values)); // => [6, 3]
A Python implementation, with a couple of points
Avoids using a reduce to make it easier to see the algorithm
First and last items are considered peaks (this can be a requirement, e.g. in interviews)
All values in the plateau are added
Can start or end on an plateau
def findPeaks(points: List[int]) -> List[int]:
peaks, peak = [], []
if points[0] >= points[1]: # handle first
peak.append(points[0])
for i in range(1, len(points)):
prv = points[i - 1]
cur = points[i]
if cur > prv: # start peak
peak = [cur]
elif cur == prv: # existing peak (plateau)
peak.append(cur)
elif cur < prv and len(peak): # end peak
peaks.extend(peak)
peak = []
if len(peak) and len(peak) != len(points): # ended on a plateau
peaks.extend(peak)
return peaks
if __name__ == "__main__":
print(findPeaks([1, 2, 3, 4, 5, 4, 3, 2, 1])) # [5]
print(findPeaks([1, 2, 1, 2, 1])) # [2, 2]
print(findPeaks([8, 1, 1, 1, 1, 1, 9])) # [0, 6]
print(findPeaks([1, 1, 1, 1, 1])) # []
print(findPeaks([1, 6, 6, 6, 1])) # [6, 6, 6]
const values = [3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3];
const findMinimas = arr => arr.filter((el, index) => {
return el < arr[index - 1] && el < arr[index + 1]
});
console.log(findMinimas(values)); // => [2, 1, 1]
const findMinimumIndices = arr => arr.map((el, index) => {
return el < arr[index - 1] && el < arr[index + 1] ? index : null
}).filter(i => i != null);
console.log(findMinimumIndices(values)); // => [1, 5, 9]

Categories