I have an object like this:
const object = {
detectors: [1, 2],
responders: [4, 22],
activators: [5, 23, 31],
enablers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
upgraders: [14, 15, 16, 17, 18, 19, 20, 21, 22],
catalyzer: [12, 29],
chains: [27],
trappers: [13],
finishers: [16],
}
Expected output :
[
{
'detectors': 1,
'responders': 4,
'activators': 5,
'enablers': 1,
'upgraders': 23,
'catalyzer': 12,
'chains': 27,
'trappers': 13,
'finishers': 16,
},
{
'detectors': 2,
'responders': 4,
'activators': 5,
'enablers': 1,
'upgraders': 23,
'catalyzer': 12,
'chains': 27,
'trappers': 13,
'finishers': 16,
},
{
'detectors': 1,
'responders': 22,
'activators': 5,
'enablers': 1,
'upgraders': 23,
'catalyzer': 12,
'chains': 27,
'trappers': 13,
'finishers': 16,
},
{...
And I already wrote a function like this:
object.activators.map((activator, i) => {
return object.detectors.map((detector, i) => {
return object.responders.map((responder, i) => {
return {
detectors: detector,
responders: responder,
activators: activator,
};
});
});
});
I can write another function to flatten the output of the code above, but is there any other way to write the code above into a more general function (not hardcoded) that can apply to any object?
You can use a recursive function to get all permutations from the entries.
const object = {
detectors: [1, 2, 3],
responders: [4, 22],
activators: [1, 2, 3, 4]
};
const getPermutations = obj => {
const res = [];
const entries = Object.entries(obj);
const go = (curr, idx) => {
const key = entries[idx][0];
for(const val of entries[idx][1]){
const next = {...curr, [key]: val};
if(idx !== entries.length - 1) go(next, idx + 1);
else res.push(next);
}
};
go({}, 0);
return res;
}
console.log(getPermutations(object));
Related
I'm trying to sort multiple arrays within an array (which also has to be shuffled). A simplified example is:
let toShuffle = [
[1, 2, 3, 4, 5],
[9, 8, 7, 6, 5],
[10, 67, 19 ,27]
...
];
const shuffled = shuffle(toShuffle);
// outout would look something like:
// [
// [8, 6, 5, 7, 9],
// [4, 3, 1, 5, 2],
// [19, 26, 10, 67],
// ...
// ]
This needs to be flexible, so any number of arrays with any amount of values should be valid.
Here is what I've tried:
function shuffle(a) {
for (let e in a) {
if (Array.isArray(a[e])) {
a[e] = shuffle(a[e]);
} else {
a.splice(e, 1);
a.splice(Math.floor(Math.random() * a.length), 0, a[e]);
}
}
return a;
}
console.log("Shuffled: " + shuffle([
[1, 2, 3, 4, 5],
[5, 4, 3, 2, 1]
]))
But it's not working as intended. Is their an easier way to do this? Or is my code correct and just buggy.
You can use Array.from() to create a new shallow-copied array and then to shuffle Array.prototype.sort() combined with Math.random()
Code:
const toShuffle = [
[1, 2, 3, 4, 5],
[9, 8, 7, 6, 5],
[10, 67, 19 ,27]
]
const shuffle = a => Array.from(a).sort(() => .5 - Math.random())
const result = toShuffle.map(shuffle)
console.log('Shuffled:', JSON.stringify(result))
console.log('To shuffle:', JSON.stringify(toShuffle))
You almost got it. The problem is that you are removing one item from an array, instead of capturing the removed item and them placing in a random position:
let toShuffle = [
[1, 2, 3, 4, 5],
[9, 8, 7, 6, 5],
[10, 67, 19 ,27]
];
function shuffle(a) {
a = [...a]; //clone array
for (let e in a) {
if (Array.isArray(a[e])) {
a[e] = shuffle(a[e]);
} else {
a.splice(~~(Math.random() * a.length), 0, a.splice(e, 1)[0]);
}
}
return a;
}
console.log(JSON.stringify(shuffle(toShuffle)))
console.log(JSON.stringify(toShuffle))
[EDIT]
The original code did not shuffle the parent array, if you need shuffle everything recursively, you can use this:
let toShuffle = [
[1, 2, 3, 4, 5],
[9, 8, 7, 6, 5],
[10, 67, 19 ,27]
];
function shuffle(a) {
a = a.map(i => Array.isArray(i) ? shuffle(i) : i); //clone array
a.sort(i => ~~(Math.random() * 2) - 1); //shuffle
return a;
}
console.log("shuffled", JSON.stringify(shuffle(toShuffle)))
console.log("original", JSON.stringify(toShuffle))
I have two arrays a and b.
Either array can have any number of items. However their length may not match.
I need the array lengths to match so I can zip the two array together.
For example:
a = [1, 2, 3, 4]
and
b = [1, 2]
Becomes:
a = [1, 2, 3, 4]
and
b = [1, 1, 2, 2]
I need b to match the length of a or vice versa to whatever one is longer length.
As well as to spread the values of the shorter array until matches the length of the longer array.
The spread on the shorter array would only contain the values present at start.
For example:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
and
b = [1, 2]
Becomes
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
and
b = [1, 1, 1, 1, 1, 2, 2, 2, 2, 2]
Another example:
a = [21, 22, 23, 24, 25, 26, 27]
and
b = [39, 40, 41, 42]
Becomes:
a = [21, 22, 23, 24, 25, 26, 27]
and
b = [39, 39, 40, 40, 41, 41, 42]
SOLVED IT using Ramda
const a = [1, 2]
const b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
R.sort(R.gte, R.flatten(R.repeat(a, b.length / 2)))
Without relying on any libraries, this function will give you the desired result
const a = [21, 22, 23, 24, 25, 26, 27]
const b = [39, 40, 41, 42]
// a, b = longer array, shorter array
function spread(a, b) {
let result = null
if (a.length !== b.length) {
// target: array that needs to be spread
const [target, longest] = a.length > b.length ? [b, a] : [a, b]
// difference: amount target needs to be spread
const difference = longest.length - target.length
// check if elements need to be repeated more than twice
if (difference > target.length) {
result = [].concat(
...target.map((n, i) => {
if (typeof n !== 'string') {
return Array.from(n.toString().repeat(difference / 2)).map(Number)
}
return Array.from(n.repeat(difference / 2))
})
)
} else {
// repeat N elements twice until N <= difference/2
result = [].concat(
...target.map((n, i) => (i <= difference / 2 ? [n, n] : n))
)
}
// return the spread array
return result
}
// return original array if both arrays are same length
return b
}
spread(a, b) // => [ 39, 39, 40, 40, 41, 42 ]
Pure JavaScript solution that will extend a shorter array to the length of a longer one. The stretching is done by repeating each value in the shorter array and dynamically re-calculating how many times this is needed. So with lengths 10 and 3, the shorter array will have the first item repeated three times but the rest only two times in order to fit:
longer length: 10
shorter: [ 1, 2, 3 ]
/|\ /| |\
/ | \ / | | \
result: [ 1, 1, 1, 2, 2, 3, 3 ]
function equaliseLength(a, b) {
const [shorter, longer] = [a, b].sort((x, y) => x.length - y.length);
let remaining = longer.length;
const stretchedArray = shorter.flatMap((item, index) => {
//how many we need of this element
const repeat = Math.ceil(remaining / (shorter.length - index));
//adjust the remaining
remaining -= repeat;
//generate an array with the element repeated
return Array(repeat).fill(item)
});
//return to the order of the input:
//if `a` was the longer array, it goes first
//otherwise flip them
return longer === a ?
[longer, stretchedArray] :
[stretchedArray, longer]
}
console.log(printResult(
[1, 2, 3, 4],
[1, 2]
));
console.log(printResult(
[21, 22, 23, 24, 25, 26, 27],
[39, 40, 41, 42]
));
console.log(printResult(
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2]
));
console.log(printResult(
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3]
));
console.log(printResult(
[1, 2, 3],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
));
console.log(printResult(
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
));
//just to make the display better
function printResult(a, b) {
const [resultA, resultB] = equaliseLength(a, b)
.map(x => x.map(y => String(y).padStart(2)))
.map(x => x.join("|"))
return `a = ${JSON.stringify(a)} b = ${JSON.stringify(b)}
result:
a = |${resultA}|
b = |${resultB}|`;
}
If I have these corresponding array with 2 nested arrays (this may have 2 or more) inside:
const nums = [
[4, 23, 20, 23, 6, 8, 4, 0], // Each array consists of 8 items
[7, 5, 2, 2, 0, 0, 0, 0]
];
How can I perform addition with them in accordance to their indexes ?
Expected Result:
// 11 is from 4 (array1 - index1) + 7 (array2 - index1)
// and so on.
[11, 28, 22, 25, 6, 8, 4, 0]
What I did was:
// This will work but it will only be applicable for 2 arrays as what if there will be 2 or more making it dynamic
const total = Array.from({ length: 8 }, (_, i) => nums[0][i] + nums[1][i]);
This support n nested arrays
const nums = [
[4, 23, 20, 23, 6, 8, 4, 0],
[7, 5, 2, 2, 0, 0, 0, 0],
[2, 1, 2, 5, 7, 8, 9, 4]
];
const total = nums.reduce((a, b) => a.map((c, i) => c + b[i]));
console.log(total);
One possible solution is to Array.map() each element of the first inner array to the sum of elements in the same column. For get the summatory of elements in the same column we can use Array.reduce() inside the map():
const nums = [
[4, 23, 20, 23, 6, 8, 4, 0],
[7, 5, 2, 2, 0, 0, 0, 0],
[1, 3, 4, 7, 1, 1, 1, 1],
];
let [first, ...rest] = nums;
let res = first.map((e, i) => rest.reduce((sum, x) => sum + x[i], e));
console.log(res);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
You can use nested forEach() loop
const nums = [
[4, 23, 20, 23, 6, 8, 4, 0],
[7, 5, 2, 2, 0, 0, 0, 0]
];
function sum(arr){
let max = Math.max(...arr.map(x => x.length));
let res = Array(max).fill(0)
res.forEach((x,i) => {
nums.forEach(a => {
res[i] = res[i] + (a[i] || 0)
})
})
return res;
}
console.log(sum(nums))
You can use reduce and an inner loop. Some of the things to be careful of are different array lengths & values that are not numbers.
const nums = [
[4, 23, 20, 23, 6, 8, 4, 0], // Each array consists of 8 items
[7, 5, 2, 2, 0, 0, 0, 0]
];
const otherNums = [
[4, 23, 20, 23, 6, 8, 4, 0, 9, 55], // Each array consists of 8 items
[7, 5, 2, 2, 0, 0, 0, 0, "cat", null, 78],
[7, 5, 2, 2, 0, 0, 0, 0, "dog", null, 78],
[7, 5, 2, 2, 0, 0, 0, 0, "elephant", null, 78]
];
const sumArraysByIndex = nums => nums.reduce((sums, array) => {
for (const index in array) {
if (sums[index] === undefined) sums[index] = 0
if (isNaN(array[index])) return sums
sums[index] += array[index]
}
return sums
}, [])
console.log(sumArraysByIndex(nums))
console.log(sumArraysByIndex(otherNums))
Get the minimum length of subarray and then create the array of that length, using index return the addition.
const nums = [
[4, 23, 20, 23, 6, 8, 4, 0],
[7, 5, 2, 2, 0, 0, 0, 0]
];
const [arr1, arr2] = nums;
const min = Math.min(nums[0].length, nums[1].length);
const output = Array.from({length: min}, (_, i) => arr1[i] + arr2[i]);
console.log(output);
I am given an input array need to convert to output array (it's consecutive array in the output array.)
var input = [1, 3, 4, 5, 8, 9, 15, 20, 21, 22, 23, 24, 25, 26, 40];
var output = [[1], [3, 4, 5], [8, 9], [15], [20, 21, 22, 23, 24, 25, 26], [40]];
I am able to achieve this by:
let t = 0;
let tArr = []
const a = [];
input.map(i => {
if (i-t === 1) {
tArr.push(i);
} else {
a.push(tArr);
tArr = [];
tArr.push(i)
}
t = i;
});
a.push(tArr);
console.log("final", a)
Can someone suggest a cleaner code or if this could be optimized.
You could reduce the array by looking at the index or ar the former value and compare to the actual value.
var input = [1, 3, 4, 5, 8, 9, 15, 20, 21, 22, 23, 24, 25, 26, 40],
result = input.reduce((r, v, i, a) => {
if (!i || a[i - 1] + 1 < v) {
r.push([v]);
} else {
r[r.length - 1].push(v);
}
return r;
}, []);
console.log(result);
I have an array which can be nested multiple times. However, always two arrays with two entries each are at the end of each "nesting". I always need the two entries from the two arrays at the end of each nesting returned.
Here is an example:
const arr = [
[
[1, 2], [3, 4]
], [
[5, 6], [7, 8]
], [
[
[9, 10], [11, 12]
], [
[14, 15], [16, 17]
]
]
];
Here is the expected result:
const return1 = [
{ a: 1, b: 2 },
{ a: 3, b: 4 }
];
const return2 = [
{ a: 5, b: 6 },
{ a: 7, b: 8 }
];
const return3 = [
{ a: 9, b: 10 },
{ a: 11, b: 12 }
];
const return4 = [
{ a: 13, b: 14 },
{ a: 15, b: 16 }
];
Everything I find online is how to reduce an n-nested array to a flat array, something like this:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
You could map with an iterative and recursive approach while checking nested arrays.
var array = [[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[[9, 10], [11, 12]], [[14, 15], [16, 17]]]],
result = array.reduce(function iter(r, a) {
return r.concat(Array.isArray((a[0] || [])[0])
? a.reduce(iter, [])
: [a.map(([a, b]) => ({ a, b }))]
);
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
With custom recursive function:
var arr = [[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[[9, 10], [11, 12]], [[14, 15], [16, 17]]]],
result = [],
get_pairs = function(arr, r){
arr.forEach(function(v){
if (Array.isArray(v)) {
if (!Array.isArray(v[0])) {
var o = {a: v[0], b: v[1]};
(!r.length || r[r.length-1].length==2)? r.push([o]) : r[r.length-1].push(o);
} else {
get_pairs(v, r);
}
}
});
};
get_pairs(arr, result);
console.log(result);
Spent to much time in this. However, here is a very messy looking code.
There is this recursive function that checks if a given array is in the form [[number, number],[number, number]]. If so, it adds an object to the variable returnArray that we are knowingly mutating.
If it is not in the form, we just check for the items inside the array.
const arrInput = [
[[1, 2], [3, 4]],
[[5, 6], [7, 8]],
[
[[9, 10], [11, 12]],
[[14, 15], [16, 17]],
],
];
function mapArrayToObj(arr, returnArray = []) {
if (arr.length === 2 && typeof arr[0][0] === "number" &&
typeof arr[0][1] === "number" && typeof arr[1][0] === "number" &&
typeof arr[1][1] === "number") {
returnArray.push([
{ a: arr[0][0], b: arr[0][1] },
{ a: arr[1][0], b: arr[1][1] }
]);
} else {
arr.forEach((item) => { mapArrayToObj(item, returnArray); });
}
return returnArray;
}
console.log(mapArrayToObj(arrInput));