How to use recursive function to reordered array with even indices first and then odd indices?
For example:
input: [5, 2, 4, 9]
output: [5, 4, 2, 9]
I want to improve this code.
let arrayB = [],
arrayOdd = [],
arrayEven = [];
let i = 0;
const reorder = (arrayA) => {
if (arrayA.length >= 2) {
if (i < arrayA.length) {
i === 0 || i % 2 === 0 ? arrayEven.push(arrayA[i]) : arrayOdd.push(arrayA[i]);
i++;
arrayB = [...arrayEven, ...arrayOdd];
reorder(arrayA);
} else {
arrayOdd = [];
arrayEven = [];
i = 0;
}
return arrayB;
}
}
console.log(reorder([4, 8, 12, 16]));
console.log(reorder([1, 2, 3, 4, 5, 6, 7, 8, 9]));
No idea why you'd need a recursive function. This should do it:
const reorder = (a) => [...a.filter((_, i) => !(i % 2)), ...a.filter((_, i) => i % 2)];
console.log(reorder([4, 8, 12, 16]));
console.log(reorder([1, 2, 3, 4, 5, 6, 7, 8, 9]));
Because you stipulate your in comments that you have instructions to use a recursive function with minimum space requirements, here's a solution that uses Array.prototype.splice() to modify the array in place:
const reorder = (a, offset = 2) => {
if (offset < a.length) {
a.splice(offset / 2, 0, ...a.splice(offset, 1));
return reorder(a, offset + 2);
}
return a;
};
console.log(reorder([4, 8, 12, 16]));
console.log(reorder([1, 2, 3, 4, 5, 6, 7, 8, 9]));
Note that, like the Array.prototype.sort() function, the above function only returns the array for convenience. No new array is created.
I think you can do this by using sort method of array. but for that you might need to use map to get index and then converting back to values.
const isOdd = n => n % 2 === 1;
const reorder = input => input.map((value, index) => ({index, value})).sort((a,b) =>
isOdd(a.index) ? 1 : isOdd(b.index) ? -1 : 0).map(({value}) => value);
console.log(reorder([4, 8, 12, 16]));
console.log(reorder([1, 2, 3, 4, 5, 6, 7, 8, 9]));
Avoid using global variables.
const reorder = (arrayA, arrayOdd = [], arrayEven = []) => {
if (arrayA.length >= 1)
arrayEven.push(arrayA.shift());
if (arrayA.length >= 1) {
arrayOdd.push(arrayA.shift());
reorder(arrayA, arrayOdd, arrayEven);
}
return [...arrayEven, ...arrayOdd];
}
console.log(reorder([4, 8, 12, 16]));
console.log(reorder([1, 2, 3, 4, 5, 6, 7, 8, 9]));
I don't know why you would choose to do this recursively, but this seems to do it:
const evenThenOdd = ([e = undefined, o = undefined, ...xs], evens = [], odds = []) =>
e == undefined
? [...evens, ...odds]
: o == undefined
? evenThenOdd ( xs, [...evens, e] [odds] )
: evenThenOdd ( xs, [...evens, e], [...odds, o] )
console .log (
evenThenOdd ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
//~> [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
)
We keep evens and odds arrays of values. The base case, when there is nothing left in the array, is to just concatenate those two arrays. The other two cases are when there is one item left and where there are two or more items left. If the undefined values are legitimate elements of your array you want to handle, then add const None = Symbol(), and replace each undefined with None.
A cleaner, non-recursive solution might look like this:
const evenThenOdd = (xs) => [
...xs.filter((_, i) => i % 2 == 0),
...xs.filter((_, i) => i % 2 == 1),
]
Related
I have an array like so: [1, 2, 3, 4, 5, 6, 7, 9, 10]. I need to chunk it into different size chunks, yet with a simple pattern of: 4, 3, 3, 3, 4, 3, 3, 3 like so:
[
[ // four
1,
2,
3,
4
],
[ // three (1/3)
5,
6,
7
],
[ // three (2/3)
8,
9,
10
],
[ // three (3/3)
11,
12,
13
],
[ // four
14,
15,
16,
17
],
[ // three (1/3)
18,
19,
20
], // and so on..
]
I have tried with this code I have customized:
const arr; // my array of values
const chuncked = arr.reduce((acc, product, i) => {
if (i % 3) {
return acc;
} else if (!didFourWayReduce) {
didFourWayReduce = true;
fourWayReduces++;
if ((fourWayReduces - 1) % 2) { // only make every second a "4 row"
return [...acc, arr.slice(i, i + 3)];
} else {
return [...acc, arr.slice(i, i + 4)];
}
} else {
didFourWayReduce = false;
return [...acc, arr.slice(i, i + 3)];
}
}, []);
And it works, almost, expect that the first chunk of threes (1/3) have the last element of the chunk with 4. So 1 key is repeated every first chunk of three. Like so:
[
[
1,
2,
3,
4
],
[
4, // this one is repeated, and it shouldn't be
5,
6
]
]
You could take two indices, one for the data array and one for sizes. Then slice the array with a given length and push the chunk to the chunks array.
Proceed until end of data.
var data = Array.from({ length: 26 }, (_, i) => i + 1),
sizes = [4, 3, 3, 3],
i = 0,
j = 0,
chunks = [];
while (i < data.length) chunks.push(data.slice(i, i += sizes[j++ % sizes.length]));
console.log(chunks);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const arr = Array.from({ length: 100 }, (_, i) => i);
const copy = [...arr];
const sizes = [4, 3, 3, 3];
const result = [];
let i = 0;
while (i <= arr.length && copy.length) {
result.push(copy.splice(0, sizes[i % sizes.length]));
i++;
}
console.log(result);
A recursive approach is fairly elegant:
const chunks = (xs, [s, ...ss]) =>
xs.length ? [xs .slice (0, s), ... chunks (xs .slice (s), [...ss, s])] : []
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
const sizes = [4, 3, 3, 3]
console .log (chunks (data, sizes))
.as-console-wrapper { max-height: 100% !important; top: 0; }
By replacing [s, ...ss] with [...ss, s], we pass a cycled version of the sizes array, so that for instance, [4, 3, 3, 3] becomes [3, 3, 3, 4]. This makes it easy to parse step-by-step.
Mod operator to check if it should be 4 or 3. Use two arrays just to make it easier (can be done with one)
const groupIt = arr => arr.reduce(({
group,
out
}, v, i) => {
var max = out.length % 4 === 0 ? 4 : 3
group.push(v)
if (group.length === max || i === arr.length - 1) {
out.push(group)
group = []
}
return {
group,
out
}
}, {
group: [],
out: []
}).out
console.log(groupIt([1, 2, 3, 4, 5, 6, 7, 8]))
var test = (new Array(30)).fill(0).map((x,i) => i + 1)
console.log(groupIt(test))
with just one:
const groupIt = arr => arr.reduce((out, v, i) => {
var max = (out.length - 1) % 4 === 0 ? 4 : 3
out[out.length - 1].push(v)
if (out[out.length - 1].length === max) {
out.push([])
}
return out
}, [[]])
console.log(groupIt([1, 2, 3, 4, 5, 6, 7, 8]))
var test = (new Array(30)).fill(0).map((x, i) => i + 1)
console.log(groupIt(test))
This answer is similar to that of Nina Scholz, but uses a for loop, which I personally find more clear.
const arr = Array.from({length: 100}, (_, i) => i + 1);
const sizes = [4, 3, 3, 3];
const result = [];
for (let i = 0, j = 0; i < arr.length; i += sizes[j], j = (j + 1) % sizes.length) {
result.push(arr.slice(i, i + sizes[j]));
}
console.log(result);
I'm trying to find the smallest common multiple from one array by comparing it to the values in the other by using % num === 0. The answer should be 6, but because each number in the first array is true at least once, they all get returned. How do I find the value that is only true and never false?
let arr = [1, 2, 3]
function test(arr) {
let x = [1, 2, 3, 4, 5, 6, 7, 8]
for (let n of arr) {
return x.filter(k => k % n === 0)
}
}
console.log(test(arr))
You need loop over the x array and return the first element that gets divided by every value in arr.
let arr = [1, 2, 3];
function test(arr) {
let x = [1, 2, 3, 4, 5, 6, 7, 8];
for (let n of x) {
if (arr.every((a) => n % a === 0)) {
return n;
}
}
}
console.log(test(arr));
You can also simply the solution using Array.prototype.find.
const
arr = [1, 2, 3],
test = (arr) =>
[1, 2, 3, 4, 5, 6, 7, 8].find((n) => arr.every((a) => n % a === 0));
console.log(test(arr));
Note: If x is not sorted, then you will have to sort it first.
const arr = [1, 2, 3],
test = (arr) =>
[8, 7, 6, 5, 4, 3, 2, 1]
.sort((a, b) => a - b)
.find((n) => arr.every((a) => n % a === 0));
console.log(test(arr));
Update based on OP's comment
You can use Array.prototype.filter and Array.prototype.some.
const arr = [1, 2, 3],
test = (arr) =>
[1, 2, 3, 4, 5, 6, 7, 8].filter((n) => arr.some((a) => n / a === 2));
console.log(test(arr));
If x is sorted can use find and every
let arr = [1, 2, 3]
let x = [1, 2, 3, 4, 5, 6, 7, 8, 12]
let res = x.find(n => arr.every(a => n%a === 0))
console.log(res)
if unsorted x
let arr = [1, 2, 3]
let x = [1, 12, 6, 4, 2, 7, 8]
let res = [...x].sort((a,b)=> a-b).find(n => arr.every(a => n%a === 0))
console.log(res)
Filter and intersect
let arr = [1, 2, 3]
let x = [1, 2, 3, 4, 5, 6, 7, 8]
function test(arr) {
let common = []
arr.forEach(n => common.push(x.filter(k => k % n === 0)))
return common.reduce((acc, cur) => acc.filter(e => cur.includes(e)));
}
console.log(test(arr))
Late to the party answer
You could solved this with one line of code using Array.reduce. Here we initialize it with an empty array and then use a ternary operator to append values that pass the remainder test. Note that we don't need to check for zero (c % n === 0) because we treat the result as a boolean.
x.reduce((p,c) => c % n ? p : p.concat([c]), [])
// TEST
const x = [1, 2, 3, 4, 5, 6, 7, 8];
[1, 2, 3].forEach(n => {
console.log(
"n =" + n,
x.reduce((p,c) => c % n ? p : p.concat([c]), [])
);
});
My input is an array like so:
[7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7]
I want to group together the numbers and add them, but by neighbors, not by total in the array. So the output would be:
['7:4', '4:2', '5:3', 1, 9, 2, '7:2']
I've attempted a few different methods using reduce, and gotten close but using the built-in Javascript methods I end up counting ALL in the array, not by neighbors.
const firstArray = [7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7];
const masterArray = [];
const unique = new Set (numberArray); // Set {7, 4, 5, 1, 9, 2, 7}
unique.forEach(u => {
masterArray.push(numberArray.filter(e => e === u));
});
console.log(masterArray);
Set is obviously wrong to use here because that gets the unique values and counts them, but I want to do it by neighbor only. So then I think I should be using a reduce but I run into the same problem.
var test = [7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7];
console.log(
test.reduce((acc, element) => {
if (acc.length && acc[acc.length - 1].key == element) {
acc[acc.length - 1].count++;
} else {
acc.push({ key: element, count: 1 });
}
return acc;
}, []).map(element => `${element.key}:${element.count}`)
);
So the logic first reduces the number to an array, tracking the last key and count. So long as the key is the same, the count is incremented. Once the key changes, a new object is pushed to start the next run. After the reduce is done, a map is performed to convert the key and counts into the desired strings.
You'll need to keep track of the last number iterated over in a persistent variable, as well as the number of occurrences of the last number which gets incremented:
const arr = [7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7];
let lastNum = arr[0];
let count = 0;
const results = [];
const doPush = () => {
results.push(
count === 1
? lastNum
: `${lastNum}:${count}`
);
};
for (const num of arr) {
if (num !== lastNum) {
doPush();
lastNum = num;
count = 1;
} else count++;
}
doPush();
console.log(results);
You could reduce the array by having a look to the last value a the index before the actual value.
const
array = [7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7],
result = array.reduce((r, value, i, { [i - 1]: last }) => {
let count = 0;
if (last === value) count = (+r.pop().toString().split(':')[1] || 0) + 1;
return [...r, count ? `${value}:${count}`: value];
}, []);
console.log(result);
Here is a solution that uses a recursive function to group the neighbors, and then Array.prototype.map() to format with a colon:
const arr = [7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7],
output = (function groupNeighbors([first, ...rest], output = [], last) {
last === first ? output[output.length - 1].push(first) : output.push([first]);
return rest.length ? groupNeighbors(rest, output, first) : output;
})(arr).map(({ [0]: num, length }) => length > 1 ? [num, length].join(':') : num);
console.log(output);
As it uses recursion, it is limited in terms of input array length, you can find the stack size limits per browser here: Browser Javascript Stack size limit
Variant with Array.prototype.reduce() (slightly shortest, no recursion, unlimited input length):
const arr = [7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7],
output = arr
.reduce(
(acc, cur, i, { [i - 1]: last }) =>
(cur === last ? acc[acc.length - 1].push(cur) : acc.push([cur])) && acc,
[]
)
.map(({ [0]: num, length }) => length > 1 ? [num, length].join(':') : num);
console.log(output);
Yes, the proper choice here is reduce:
const countDuplicatesQuantity = (arr) => {
const duplicatesObj = arr.reduce((duplicates, item) => {
duplicates[item] = duplicates[item] ? duplicates[item] + 1 : 1;
return duplicates;
}, {});
return Object.keys(duplicatesObj).map(
(key) => `${key}:${duplicatesObj[key]}`,
);
};
You wanted reduce, how about twice? :) (I don't know if I've done something stupid here.)
First reduce finds where the values change in the array, second uses that to build new array.
const firstArray = [7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7];
const masterArray = firstArray
.reduce((acc, v, i, all) => all[i + 1] !== v ? acc.concat(i) : acc, [])
.reduce(
(acc, v, i, all) => {
const range = v - (i === 0 ? -1 : all[i - 1]);
return acc.concat(range > 1 ? firstArray[v] + ':' + range : firstArray[v]);
}, []
);
console.log(masterArray);
Using array.reduce:
const firstArray = [7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7];
var newArray = [];
var count = 0;
firstArray.reduce((acc, cur, index) => {
if (acc == cur) {
count++;
}
if (acc != cur || index+1 == firstArray.length) {
newArray.push(acc + ":" + count);
count=1
}
return cur;
})
console.log(newArray);
my way ... I felt that there could be "simpler"
const firstArray = [7, 7, 7, 7, 4, 4, 5, 5, 5, 1, 9, 2, 7, 7];
const result = firstArray.reduceRight((r,v)=>
{
let [n,c] = !r.length ? [-v,0] : isNaN(r[0]) ? r[0].split(':') : [r[0],1]
if (n==v) r[0] = `${n}:${++c}`
else r.unshift(v)
return r
},[])
console.log( JSON.stringify(result) )
// [ "7:4", "4:2", "5:3", 1, 9, 2, "7:2" ]
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an array like so: [1, 2, 3, 4, 5, 6, 7, 9, 10]. I need to chunk it into different size chunks, yet with a simple pattern of: 4, 3, 3, 3, 4, 3, 3, 3 like so:
[
[ // four
1,
2,
3,
4
],
[ // three (1/3)
5,
6,
7
],
[ // three (2/3)
8,
9,
10
],
[ // three (3/3)
11,
12,
13
],
[ // four
14,
15,
16,
17
],
[ // three (1/3)
18,
19,
20
], // and so on..
]
I have tried with this code I have customized:
const arr; // my array of values
const chuncked = arr.reduce((acc, product, i) => {
if (i % 3) {
return acc;
} else if (!didFourWayReduce) {
didFourWayReduce = true;
fourWayReduces++;
if ((fourWayReduces - 1) % 2) { // only make every second a "4 row"
return [...acc, arr.slice(i, i + 3)];
} else {
return [...acc, arr.slice(i, i + 4)];
}
} else {
didFourWayReduce = false;
return [...acc, arr.slice(i, i + 3)];
}
}, []);
And it works, almost, expect that the first chunk of threes (1/3) have the last element of the chunk with 4. So 1 key is repeated every first chunk of three. Like so:
[
[
1,
2,
3,
4
],
[
4, // this one is repeated, and it shouldn't be
5,
6
]
]
You could take two indices, one for the data array and one for sizes. Then slice the array with a given length and push the chunk to the chunks array.
Proceed until end of data.
var data = Array.from({ length: 26 }, (_, i) => i + 1),
sizes = [4, 3, 3, 3],
i = 0,
j = 0,
chunks = [];
while (i < data.length) chunks.push(data.slice(i, i += sizes[j++ % sizes.length]));
console.log(chunks);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const arr = Array.from({ length: 100 }, (_, i) => i);
const copy = [...arr];
const sizes = [4, 3, 3, 3];
const result = [];
let i = 0;
while (i <= arr.length && copy.length) {
result.push(copy.splice(0, sizes[i % sizes.length]));
i++;
}
console.log(result);
A recursive approach is fairly elegant:
const chunks = (xs, [s, ...ss]) =>
xs.length ? [xs .slice (0, s), ... chunks (xs .slice (s), [...ss, s])] : []
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
const sizes = [4, 3, 3, 3]
console .log (chunks (data, sizes))
.as-console-wrapper { max-height: 100% !important; top: 0; }
By replacing [s, ...ss] with [...ss, s], we pass a cycled version of the sizes array, so that for instance, [4, 3, 3, 3] becomes [3, 3, 3, 4]. This makes it easy to parse step-by-step.
Mod operator to check if it should be 4 or 3. Use two arrays just to make it easier (can be done with one)
const groupIt = arr => arr.reduce(({
group,
out
}, v, i) => {
var max = out.length % 4 === 0 ? 4 : 3
group.push(v)
if (group.length === max || i === arr.length - 1) {
out.push(group)
group = []
}
return {
group,
out
}
}, {
group: [],
out: []
}).out
console.log(groupIt([1, 2, 3, 4, 5, 6, 7, 8]))
var test = (new Array(30)).fill(0).map((x,i) => i + 1)
console.log(groupIt(test))
with just one:
const groupIt = arr => arr.reduce((out, v, i) => {
var max = (out.length - 1) % 4 === 0 ? 4 : 3
out[out.length - 1].push(v)
if (out[out.length - 1].length === max) {
out.push([])
}
return out
}, [[]])
console.log(groupIt([1, 2, 3, 4, 5, 6, 7, 8]))
var test = (new Array(30)).fill(0).map((x, i) => i + 1)
console.log(groupIt(test))
This answer is similar to that of Nina Scholz, but uses a for loop, which I personally find more clear.
const arr = Array.from({length: 100}, (_, i) => i + 1);
const sizes = [4, 3, 3, 3];
const result = [];
for (let i = 0, j = 0; i < arr.length; i += sizes[j], j = (j + 1) % sizes.length) {
result.push(arr.slice(i, i + sizes[j]));
}
console.log(result);
I'm wanting to create a function which accepts 2 arguments, first argument is an array, second argument is a number of index positions to move all the array items.
So for example if I passed exampleFunc([1,2,3,4,5], 2) it should move all items 2 places to the right, so returns [4,5,1,2,3]. I've done the following, however is there a more eloquent / efficient way of doing this? Also if I wanted to reverse the direction and condense into 1 function and not two as done below, any suggestions how to do this other than putting conditionals around the different part of each function? Tried using .splice() method but didn't really got anywhere. Any help would really be appreciated!
const moveArrayPositionRight = (array, movePositions) => {
let newArray = new Array(array.length);
for (i = 0; i < array.length; i++) {
let newIndex = i - movePositions;
if (newIndex < 0) {
newIndex += array.length;
}
newArray[i] = array[newIndex];
}
return newArray;
};
console.log(moveArrayPositionRight([2, 4, 6, 8, 10], 2)); // output: [8, 10, 2, 4, 6]
const moveArrayPositionLeft = (array, movePositions) => {
let newArray = new Array(array.length);
for (i = 0; i < array.length; i++) {
let newIndex = i - movePositions;
if (newIndex < 0) {
newIndex += array.length - 1;
}
newArray[i] = array[newIndex];
}
return newArray;
};
console.log(moveArrayPositionLeft([3, 6, 9, 12, 15], 2)); // output: [9,12,15,3,6]
You have the index of the position where you want to slice the array up and rearrange it, so you can use .slice to do exactly that - extract the sub-arrays that need to be rearranged, and put into a new array:
const moveArrayPositionRight = (array, movePositions) => [
...array.slice(array.length - movePositions),
...array.slice(0, array.length - movePositions)
];
console.log(moveArrayPositionRight([2, 4, 6, 8, 10], 2)); // output: [8, 10, 2, 4, 6]
console.log(moveArrayPositionRight([2, 4, 6, 8, 10], 3)); // expected [6, 8, 10, 2, 4]
.slice can also take negative indicies to slice an amount from the end instead of from the beginning:
const moveArrayPositionRight = (array, movePositions) => [
...array.slice(-movePositions),
...array.slice(0, -movePositions)
];
console.log(moveArrayPositionRight([2, 4, 6, 8, 10], 2)); // output: [8, 10, 2, 4, 6]
console.log(moveArrayPositionRight([2, 4, 6, 8, 10], 3)); // expected [6, 8, 10, 2, 4]
Can also use .concat instead of spread
const moveArrayPositionRight = (array, movePositions) => array
.slice(array.length - movePositions)
.concat(array.slice(0, array.length - movePositions));
console.log(moveArrayPositionRight([2, 4, 6, 8, 10], 2)); // output: [8, 10, 2, 4, 6]
console.log(moveArrayPositionRight([2, 4, 6, 8, 10], 3)); // expected [6, 8, 10, 2, 4]
Same sort of thing for moveArrayPositionLeft:
const moveArrayPositionLeft = (array, movePositions) => [
...array.slice(movePositions),
...array.slice(0, movePositions)
];
console.log(moveArrayPositionLeft([3, 6, 9, 12, 15], 2)); // output: [9,12,15,3,6]