I'm struggling to find a solution the following code challenge that meets all of the requirements and could use some help:
var ex1 = [5, 5, 2, 1, *4*, 2, *6*, 2, 1, 2, 7, 7];
// {pos:[4,6], peaks:[4,6] }
var ex2 = [3, 2, 3, *6*, 4, 1, 2, *3*, 2, 1, 2, 3];
// {pos:[3,7], peaks:[6,3]}
var plateau = [1, *2*, 2, 2, 1];
// {pos:[1],peaks[2]}
Find the local maxima or "peaks" of a given array but ignore local
maxima at the beginning and end of the array.
If there is a "plateau", return the position and value at the
beginning of the "plateau."
Any plateaus at beginning and end of the array should be ignored.
My proposed solution uses the reduce function to look at the elements in the array before and after the current element. If these values are less than the value of the current element, the current element is a peak. "Peaks" at the edges of the array are ignored because they do not satisfy either the first or second criteria.
function pickPeaks(array) {
return array.reduce((res, curr, i, arr) => {
if(arr[i-1] < curr && curr > arr[i+1]) {
res["pos"] = res["pos"] ? res["pos"].concat([i]) : [i];
res["peaks"] = res["peaks"] ? res["peaks"].concat([curr]) : [curr];
}
return res;
},{});
}
However, this solution fails to find plateaus.
If I change my "right side" conditional logic to curr >= arr[i+1] it finds plateaus but does not ignore "edge" plateaus like so:
var plateau = [1, *2*, 2, 2, 1];
correct // {pos:[1],peaks[2]}
var ex1 = [5, 5, 2, 1, *4*, 2, *6*, 2, 1, 2, *7*, 7];
incorrect // {pos:[4,6,7], peaks:[4,6,10]}
What am I missing here? How can I check if a "plateau" is at the edge of the array or not?
You could add a while loop for getting the end of a plateau.
function getLocalMaxima(array) {
return array.reduce(function (r, v, i, a) {
var j = i;
while (v === a[++j]);
if (a[i - 1] < v && (a[i + 1] < v || a[i + 1] === v && a[j] < v)) {
r.pos.push(i);
r.peaks.push(v);
}
return r;
}, { pos: [], peaks: []});
}
var ex1 = [5, 5, 2, 1, 4, 2, 6, 2, 1, 2, 7, 7], // { pos: [4, 6], peaks:[4, 6] }
ex2 = [3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3], // { pos: [3, 7], peaks: [6, 3]}
plateau = [1, 2, 2, 2, 1]; // { pos: [1], peaks[2] }
console.log(getLocalMaxima(ex1));
console.log(getLocalMaxima(ex2));
console.log(getLocalMaxima(plateau));
.as-console-wrapper { max-height: 100% !important; top: 0; }
My solution is to remove plateaus at the end in advance, and a minor correction on your updating the result of peaks:
function trimEndingPlateaus(array){
var end = array.length -1;
for (var i = 1; i < array.length -1; i++){
if (array[array.length - 1 - i] === array[end]) end--;
else break;
}
return array.slice(0, end);
}
function pickPeaks(array) {
return trimEndingPlateaus(array).reduce((res, curr, i, arr) => {
// find peaks
if(arr[i-1] < curr && curr >= arr[i+1]) {
res["pos"] = res["pos"] ? res["pos"].concat([i]) : [i];
res["peaks"] = res["peaks"] ? res["peaks"].concat([curr]):[curr];
}
return res;
},{});
}
Test cases:
var plateau = [1, 2, 2, 2, 1];
var ex1 = [5, 5, 2, 1, 4, 2, 6, 2, 1, 2, 7, 7];
var ex2 = [3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3];
console.log(pickPeaks(plateau));
//Outputs: {"pos":[1],"peaks":[2]}
console.log(pickPeaks(ex1));
//Outputs: {"pos":[4,6],"peaks":[4,6]}
console.log(pickPeaks(ex2));
//Outputs: {"pos":[3,7],"peaks":[6,3]}
Related
Say I have 2 lists with identical items that I've shuffled like below:
listA = [1, 3, 2];
listB = [2, 3, 1];
I want to make sure that list items of the same index don't match. So I wouldn't want listA[1] to match with listB[1]. How do I randomize both lists so that this doesn't occur?
There is probably a more elegant way to do this, but the code below should work, even for arrays of different sizes. It first checks whether it's possible to get the uniqueness you're looking for, and if so it goes into a while loop to continuously shuffle the larger of the two arrays (in place) until it finds a solution.
// First, set up utility functions
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
function smallerAndOther(arr1, arr2) {
const smallerArr = arr1.length < arr2.length ? arr1 : arr2;
const otherArr = smallerArr === arr1 ? arr2 : arr1;
return [smallerArr, otherArr];
}
function anyEqualIdx(arr1, arr2) {
const [smallerArr, otherArr] = smallerAndOther(arr1, arr2);
for (let i of smallerArr.keys()) {
if (smallerArr[i] === otherArr[i]) return true;
}
return false;
}
function getCount(array, value) {
return array.filter((v) => (v === value)).length;
}
// Now for the real stuff
function sufficientUnique(arr1, arr2) {
const [smallerArr, otherArr] = smallerAndOther(arr1, arr2);
for (let num of new Set([...smallerArr])) {
if (otherArr.length - getCount(otherArr, num) < getCount(smallerArr, num)) {
return false;
}
}
return true;
}
function shuffleUniqueIdxs(arr1, arr2) {
if (!sufficientUnique(arr1, arr2)) {
console.log("Error: Not enough unique values to meet constraint.");
return;
}
const largerArr = arr1.length > arr2.length ? arr1 : arr2;
while (anyEqualIdx(arr1, arr2)) {
shuffleArray(largerArr);
}
console.log("Success: ", arr1, arr2);
}
// Testing
let listA = [1, 3, 2];
let listB = [2, 3, 1];
shuffleUniqueIdxs(listA, listB);
listA = [7, 5, 5, 3, 9, 9, 1];
listB = [3, 5, 5];
shuffleUniqueIdxs(listA, listB);
listA = [1, 1, 1];
listB = [2, 1, 1];
shuffleUniqueIdxs(listA, listB); // shows error message
listA = [1, 1, 1, 1, 1];
listB = [2, 2, 2, 2, 2];
shuffleUniqueIdxs(listA, listB);
listB = [99, 9, 9, 9, 9, 9, 9, 9, 99, 88, 8, 8, 8, 8, 8, 7, 7, 6, 65, 5, 5, 5, 4]
listA = [9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6];
shuffleUniqueIdxs(listA, listB);
Here's one solution. It first individually shuffles both arrays, then it looks for repeated entries and randomly moves those around. Note that this solution only works for arrays of the same size. It is also intended to be used on arrays where most elements are unique (otherwise, it might get stuck randomly moving things around for a while).
const randIntBetween = (left, right) => left + Math.floor(Math.random() * (right - left));
function shuffle(array) {
array = [...array];
for (let i = 0; i < array.length; ++i) {
const newIndex = randIntBetween(i, array.length);
[array[i], array[newIndex]] = [array[newIndex], array[i]];
}
return array;
}
function randomlyMoveRepeatedEntries(array, comparisonArray) {
array = [...array];
const indicesToCheck = new Set(array.map((_, i) => i));
while (indicesToCheck.size) {
const { value: index } = indicesToCheck.values().next();
if (array[index] !== comparisonArray[index]) {
indicesToCheck.delete(index);
continue;
}
const newIndex = randIntBetween(index, array.length);
[array[index], array[newIndex]] = [array[newIndex], array[index]];
indicesToCheck.add(newIndex);
}
return array;
}
// ----- Example Usage ----- //
const listA = shuffle([1, 2, 3, 4, 5]);
const listB = randomlyMoveRepeatedEntries(shuffle([1, 2, 3, 4, 5]), listA);
console.log(listA.join(', '));
console.log(listB.join(', '));
i have 2 rows that i need to check in java script if they are symmetrical
row 1 [2, 7, 9, 9, 7, 2] row 2 [5 7 3 3 7 5] how would you do it ?
var r = [[5, 7, 3, 3, 7, 5], [2, 7, 9, 9, 7, 2]];
function isSymmetric(r) {
// convert to object
var rel = {}
for (var i = 0; i < r.length; i++) {
if (!(r[i][0] in rel)) rel[r[i][0]] = {};
rel[r[i][0]][r[i][1]] = true;
}
// Test to see if opposite relation is in object
for (var a in rel) {
for (var b in rel[a]) {
if (!rel[b] || !rel[b][a]) return false;
}
}
return true;
}
console.log(isSymmetric(r));
You could do something like this:
let isSymmetric = arr => {
for(var i=0; i < arr.length; i++) {
if(arr[i] !== arr[arr.length - (i+1)])
return false
}
return true
}
console.log(isSymmetric([5, 7, 3, 3, 7, 5]))
console.log(isSymmetric([1, 7, 9, 9, 7, 2]))
The idea is to loop through the array and for each index compare with its "sibling" from the right side. If one is not the same them return false.
You can start from 0th index and compare the value with its symmetric pair (length - 1 - i), and if they are not same then return false. You should stop at the middle length / 2:
let values = [
[5, 7, 3, 3, 7, 5],
[2, 7, 9, 1, 9, 7, 2],
[5, 7, 3, 3, 7, 1]
];
function isSymmetric(arr) {
for (let i = 0; i < arr.length / 2; i++) {
if (arr[i] !== arr[arr.length - 1 - i]) {
return false;
}
}
return true;
}
values.forEach(v =>
console.log(isSymmetric(v))
);
Use lodash.
var row = [2, 7, 9, 9, 7, 2];
var halves = _.chunk(row, _.ceil(row.length / 2));
var result = _.isEqual(halves[0], _.reverse(halves[1]));
I need to find the index of the first duplicated number in an array and assign it to an empty variable using only for loop
Thanks in advance
i have tried many logical operators.
var findIndex;
var arrWithNumbers = [2, 4, 5, 2, 6, 5, 1, 2, 4, 8]; //-----> it should give result console.log(findIndex) // 0
var arrWithNumbers = [3, 4, 5, 2, 6, 5, 1, 2, 4, 8]; //-----> it should give result console.log(findIndex) // 1
var arrWithNumbers = [2, 4, 5, 2, 6, 5, 1, 2, 4, 8];
var firstIndex = null;
for (var i = 0; i < arrWithNumbers.length; i++) {
if (arrWithNumbers[i] === i) {
firstIndex = arrWithNumbers.indexOf(i);
break;
}
}
console.log(firstIndex);
what I expect:
var arrWithNumbers = [2, 4, 5, 2, 6, 5, 1, 2, 4, 8]; //-----> it should give result console.log(findIndex) // 0
var arrWithNumbers = [3, 4, 5, 2, 6, 5, 1, 2, 4, 8]; //-----> it should give result console.log(findIndex) // 1
//what i have
var arrWithNumbers = [2, 4, 5, 2, 6, 5, 1, 2, 4, 8];
var firstIndex = null;
for (var i = 0; i < arrWithNumbers.length; i++) {
if (arrWithNumbers[i] === i) {
firstIndex = arrWithNumbers.indexOf(i);
break;
}
}
console.log(firstIndex); // 2
One option you have is to have a variable that contains all the count of the number, you can do this by using reduce
var arrWithNumbers = [2, 4, 5, 2, 6, 5, 1, 2, 4, 8];
var firstIndex = null;
var numberCount = arrWithNumbers.reduce((c, v) => (c[v] = (c[v] || 0) + 1, c), {});
for (var i = 0; i < arrWithNumbers.length; i++) {
if (numberCount[arrWithNumbers[i]] > 1) {
firstIndex = i;
break;
}
}
console.log(firstIndex);
Another option is using lastIndexOf. If the current index is not the same as the lastIndexOf value, means that it has duplicate and break the loop.
var arrWithNumbers = [3, 2, 4, 5, 2, 6, 5, 1, 2, 4, 8];
var firstIndex = null;
for (var i = 0; i < arrWithNumbers.length; i++) {
if (i !== arrWithNumbers.lastIndexOf(arrWithNumbers[i])) {
firstIndex = i;
break;
}
}
console.log(firstIndex);
You could take a hash table for visited values and store their indices. Then you need only the check if the hash property is set and return the index.
This approach works with a single loop and exits early on the first found same value.
function findIndex(array) {
var indices = Object.create(null),
i, value;
for (i = 0; i < array.length; i++) {
value = array[i];
if (value in indices) return indices[value];
indices[value] = i;
}
}
console.log(findIndex([2, 4, 5, 2, 6, 5, 1, 2, 4, 8])); // 0
console.log(findIndex([3, 4, 5, 2, 6, 5, 1, 2, 4, 8])); // 2
You can use a nested for loop, to check all values after index i in your array :
var arrWithNumbers = [2, 4, 5, 2, 6, 5, 1, 2, 4, 8];
var firstIndex = null;
for (var i = 0; i < arrWithNumbers.length; i++) {
value_i = arrWithNumbers[i]
// loop through the next items of the array
for (var j = i+1 ; j < arrWithNumbers.length; j++) {
if (value_i == arrWithNumbers[j]) {
firstIndex = i;
break;
}
}
if (firstIndex !== null) {
// we found our firstIndex, quit the main loop
break;
}
}
console.log(firstIndex)
I am taking an excercise on codewars:
Given a list of integers and a single sum value, return the first two
values (parse from the left please) in order of appearance that add up
to form the sum.
Example:
sum_pairs([10, 5, 2, 3, 7, 5], 10)
# ^-----------^ 5 + 5 = 10, indices: 1, 5
# ^--^ 3 + 7 = 10, indices: 3, 4 *
# * entire pair is earlier, and therefore is the correct answer
== [3, 7]
What do you think entire pair is earlier means? IMO if the sum of it's indexes is smallest. Now based on this assumption I made my solution and one test fails:
var sum_pairs=function(ints, s){
let i = 0;
let pair = [0, 0];
let ind = [100, 100]
let found = false;
function loop(i) {
if (i > ints.length) return pair;
ints.slice(i).forEach((curr, idx) => {
ints.slice(i+1).some((num, i) => {
let sum = curr + num;
let prevIndicies = ind[0] + ind[1];
if(sum === s && prevIndicies > idx + i) {
ind = [idx, i];
pair = [curr, num];
found = true;
return true;
}
})
})
i += 1;
loop(i)
}
loop(0)
if (found) {
return pair
}
return undefined;
}
console.log(sum_pairs([1,4,8,7,3,15], 8))
Test returns error that [1, 7] is expected.
I'm pretty sure what it means is they want the second element to be as leftward in the list as possible. For example, for
l5= [10, 5, 2, 3, 7, 5];
when trying to find a sum of 10, the desired output is
[3, 7]
[10, 5, 2, 3, 7, 5];
^ ^
instead of
[5, 5]
[10, 5, 2, 3, 7, 5];
^ ^
because the last element in [3, 7], the 7, came before the second 5.
This code seems to pass all test cases - iterate in a triangular fashion, starting at indicies [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3], ...:
const sum_pairs = function(ints, s){
const { length } = ints;
for (let i = 1; i < length; i++) {
for (let j = 0; j < i; j++) {
if (ints[i] + ints[j] === s) return [ints[j], ints[i]];
}
}
}
const sum_pairs=function(ints, s){
const { length } = ints;
for (let i = 1; i < length; i++) {
for (let j = 0; j < i; j++) {
if (ints[i] + ints[j] === s) return [ints[j], ints[i]];
}
}
}
l1= [1, 4, 8, 7, 3, 15];
l2= [1, -2, 3, 0, -6, 1];
l3= [20, -13, 40];
l4= [1, 2, 3, 4, 1, 0];
l5= [10, 5, 2, 3, 7, 5];
l6= [4, -2, 3, 3, 4];
l7= [0, 2, 0];
l8= [5, 9, 13, -3];
console.log(sum_pairs(l1, 8))
console.log(sum_pairs(l2, -6))
console.log(sum_pairs(l3, -7))
console.log(sum_pairs(l4, 2))
console.log(sum_pairs(l5, 10))
console.log(sum_pairs(l6, 8))
console.log(sum_pairs(l7, 0))
console.log(sum_pairs(l8, 10))
It means that you go from left to right and take the first matching pair, and since 7 is the first element that creats a pair (going from the left) 3 and 7 is the first pair.
I would solve it a bit easier:
function sum_pairs(arr, target) {
let old = [];
let result = [];
arr.some((el) => {
let found = old.find((oldEl) => oldEl + el === target);
if (found) return result = [found, el];
old.push(el);
})
return result;
}
sum_pairs([10, 5, 2, 3, 7, 5], 10);
Edit: an explaination. I loop over all elements in the array searching for a match i all the elements I have passed. If I find a match I remember it and break out of the loop by returning a "truthy" value. (That is just how .some() works.) Finally if I have not found a match I add the element to my list of old elements and go on to the next.
function sum_pair(arr,sum){
let result = [];
arr.forEach((i, j)=>{
if(i+arr[j+1]===sum){
console.log(i,arr[j+1], i+arr[j+1])
}
})
}
sum_pair([0, 3, 7, 0, 5, 5],10)
first question on stackoverflow, i'm struggling with this algorithm. This is supposed to slice my array in 5 like "[[0, 1], [2, 3], [4, 5], [6, 7], [8]]" but all i got is "[ [ 0, 1 ], [ 2, 3 ], [ 4, 5 ], [ 6, 7, 8 ] ]"
function chunkArrayInGroups(arr, size) {
var newArr = [];
console.log(Math.floor(arr.length / size));
for (i = 0; i <= (Math.floor(arr.length / size)) + 1; ++i) {
var cut = size;
newArr.push(arr.splice(0, cut));
}
if (arr.length > 0) {
newArr.push(arr.splice(0, size + (arr.length - size)));
}
return newArr;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
// expected - [[0, 1], [2, 3], [4, 5], [6, 7], [8]]
If you have any tips about the way of asking questions, i'd be happy to receive any advice !
Use a simple for loop with Array#slice, because slice doesn't change the length of the original array:
function chunkArrayInGroups(arr, size) {
var chunked = [];
for(var i = 0; i < arr.length; i += size) { // increment i by the size
chunked.push(arr.slice(i, i + size));
}
return chunked;
}
var result = chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
console.log(result);
Since you are removing element using Array#splice the array length get decreased so instead of calculating the range cache the range for the for loop condition. Although use Math.ceil and avoid the unnecessary if statement.
function chunkArrayInGroups(arr, size) {
var newArr = [],
range = Math.ceil(arr.length / size);
for (i = 0; i < range; i++) {
newArr.push(arr.splice(0, size));
}
return newArr;
}
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2));
Check this out.
function chunkArrayInGroups(arr, size) {
newArr = [];
for (i=0,j=arr.length; i<j; i+=size) {
newArr.push(arr.slice(i,i+size));
}
return newArr;
}
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2));
// expected - [[0, 1], [2, 3], [4, 5], [6, 7], [8]]
Another way to do it is Array#reduce:
function chunkArrayInGroups(arr, size) {
return arr.reduce(function (accum, elem) {
var curr = accum[accum.length - 1];
if (curr.length < size) curr.push(elem); else accum.push([elem]);
return accum;
}, [[]]);
}
var result = chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
console.log( JSON.stringify(result) );
Welcome to SO. Here is how I would do that. Let me know if you have any questions about this approach.
function chunkArrayInGroups(arr, size) {
var newArr = [];
while(arr.length > 0){
newArr.push(arr.splice(0, size));
}
return newArr;
}
Functionally you can do as follows;
function chunkArrayInGroups(a,g){
return Array(Math.ceil(a.length/g)).fill()
.map((_,i) => [a[g*i]].concat(a.slice(g*i+1, g*i+g)));
}
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8];
result = [];
result = chunkArrayInGroups(arr,2);
console.log(JSON.stringify(result));
result = chunkArrayInGroups(arr,3);
console.log(JSON.stringify(result));
result = chunkArrayInGroups(arr,4);
console.log(JSON.stringify(result));
You could use a while loop and splice the length of the wanted size for a grouped array.
function chunkArrayInGroups(array, size) {
var result = [];
while (array.length) {
result.push(array.splice(0, size));
}
return result;
}
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2));