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));
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 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)
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]}
I have a the following dict I would like to transpose:
dict = {
'column1': [1, 2, 3],
'column2': [4, 5, 6],
'column3': [7, 8, 9]
}
Into:
transposed_array = [
[1, 4, 7],
[2, 5, 8],
[3, 6, 9]
]
Here's a solution (updated for non square matrices):
var dict = {
'column1': [1, 2, 3],
'column2': [4, 5, 6],
'column3': [7, 8, 9]
}
var keys = Object.keys(dict);
var transposedMatrix = dict[keys[0]].map(function(col,i){
return keys.map(function(_,j){ return dict[keys[j]][i] })
});
Demonstration
Note that this code assumes that objects do have ordered keys, which has always been the case in all known ES implementations and is now normalized by ES6.
var dict = {
'column1': [1, 2, 3],
'column2': [4, 5, 6],
'column3': [7, 8, 9]
};
var transposed_array =[];
for (key in dict){
transposed_array.push(dict[key]);
}
console.log(transposed_array);
Demo
Updated:
var dict = {
'column1': [1, 2, 3],
'column2': [4, 5, 6],
'column3': [7, 8, 9]
};
var transposed_array =[];
for (key in dict){
transposed_array.push(dict[key]);
}
function transposeArray(array){
var newArray = [];
for(var i = 0; i < array.length; i++){
newArray.push([]);
}
for(var i = 0; i < array.length; i++){
for(var j = 0; j < array[i].length; j++){
newArray[j].push(array[i][j]);
}
}
return newArray;
}
console.log(transposeArray(transposed_array));
Demo
basically first find the shared part of the keys, then the endings, after sort reassamle the array. the result is numerical orderd if number otherwise the order is maintained.
var dict = {
'column3': [7, 8, 9],
'column2': [4, 5, 6],
'column1': [1, 2, 3]
},
keys = Object.keys(dict),
samePart = keys.reduce(function (a, b) {
var i = 0, l = Math.min(a.length, b.length);
while (i < l && a[i] === b[i]) { i++; }
return a.substr(0, i);
}),
otherPart = keys.map(function (e) { return e.slice(samePart.length); }).sort(function (a, b) { return a - b; }),
transposedMatrix = [].map.call(otherPart, function (col, i) { return dict[samePart + col].map(function (_, j) { return dict[samePart + col][j]; }); });