Sum of nested arrays according to their indexes - javascript

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);

Related

Splitting an array into 3 unequal arrays in JavaScript

Suppose I have an array of
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
And I want to split it in 3, with two arrays containing the first and last X elements of the original array, and the third array containing the remaining elements, like so:
#1 - [0, 1, 2]
#2 - [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
#3 - [13, 14, 15]
Is there a shorter/better way of doing that instead of:
const splitBy = 3;
const originalArray = Array.from(Array(16).keys());
const result = [
originalArray.slice(0, splitBy),
originalArray.slice(splitBy, -splitBy),
originalArray.slice(-splitBy),
];
console.log(result)
"better" is subjective... however, if you need this more than once, a generic function could be an option:
function multiSlice(a, ...slices) {
let res = [], i = 0
for (let s of slices) {
res.push(a.slice(i, s))
i = s
}
res.push(a.slice(i))
return res
}
// for example,
const originalArray = Array.from(Array(16).keys());
console.log(multiSlice(originalArray, 3, -3))
console.log(multiSlice(originalArray, 2, 5, 10, 12))

Shuffle nested arrays in Javascript

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))

convert 9x9 array into 9 (3x3) array in javascript?

convert 9x9 array into 9 (3x3) array in javascript?
i have written the code, but its not pushing the 3x3's into separate array.
i want 9 3x3 arrays
let array =
[
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[9, 7, 8, 3, 1, 2, 6, 4, 5]
];
let final=[];
let row = [0,1,2];
let col = [0,1,2];
let counter = 0;
for ( let i = 0 ; i <= array.length - 1 ; i += 3 )
{
for(let j = 0 ; j <= array.length - 1 ; j += 3 )
{
final.push([]);
row.forEach( ele1 => {
final[counter].push([])
col.forEach( ele2 => {
final[counter][ele1].push(array[ele1+i][ele2+j]);
})
})
counter+=1;
}
}
console.log(final)
You can loop through the array using map function and check for the index values to break into arrays as required.
let myArr =[[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[9, 7, 8, 3, 1, 2, 6, 4, 5]];
let newArr = convertTo3x3(myArr);
console.log(newArr);
function convertTo3x3(myArr){
let array3x3 = [];
myArr.map((row, rIndex) => {
let tempArr = [];
let row3 = [];
row.map((item, lIndex) => {
// convert each row to 3x3 rows
if(lIndex % 3 == 0){
// reset row3 for new 3x3 arr on every 1st item of 3x3
row3 = [];
}
row3.push(item);
if(lIndex % 3 == 2){
// push 3x3 row to tempArr on every 3rd item of 3x3
tempArr.push(row3);
}
});
array3x3.push(tempArr);
});
return array3x3;
}
Your code seems to work fine, but if you want a more javascriptey code, that performs worse, here you go.
let array = [
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[9, 7, 8, 3, 1, 2, 6, 4, 5],
];
let final = [];
array
.reduce((a, c) => {
const chunk = (arr, n) =>
arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : [];
return [...a, ...chunk(c, 3)];
}, [])
.map((el, i) => {
if (i % 3 == 0) {
final = [...final, [el]];
} else {
final[Math.floor(i / 3)] = [...final[Math.floor(i / 3)], el];
}
});
console.log(final);
Your code is trying to do too much. Make it difficult to understand.
I would first break it up. Write a simple function than knows how to take an array-of-arrays and return an arbitrary rectangular selection from it. Something like this, that's easy to test:
/**
* Snip a rectangular section of an array of arrays (ragged, 2D array).
* The returned array-of-arrys will ALWAYS be the specified size, padded
* with 'undefined' values to the specified size.
*
* #param {Object[][]} arr - An array of arrays (ragged 2D array)
* #param {number} row - Origin row: {row,col} denotes the upper-left corner of the rectangle to snip
* #param {number} col - Origin column: {row,col} denotes the upper-left corner of the rectangle to snip
* #param {number} nrows - Number of rows to snip
* #param {number} ncols - Nunber of columns to snip
*
* #returns {Object[][]}
*/
function snip(arr, row, col, nrows, ncols ) {
const selection = [];
for ( let r = row, i = 0 ; r < row+nrows ; ++r, ++i ) {
selection[i] = [];
const tmp = arr[r] ?? [];
for ( let c = col, j = 0 ; c < col+ncols ; ++c, ++j ) {
selection[i][j] = tmp[c];
}
}
return selection;
}
Once you have that, then chopping up your larger array-of-arrays into 3x3 arrays is easy.
This code starts at the top left corner or your 9x9 array and returns a flat list containing 9 separate 3x3 arrays, chopped out left-to-right and top-to-bottom:
final = [];
for ( let x = 0 ; x < 9; x += 3 ) {
for ( let y = 0 ; y < 9 ; y += 3 ) {
// (x,y) denotes the top left corner of the desired sub-array
final.push( snip(arr, x,y, 3,3 ) );
}
}
The nice thing about this approach is that it is easy to test, and
.
.
.
It's flexible. It can handle a source array of any size, and you can chop it up into subarrays of any size and in any order, whatever you see fit to do.

picking a unique set from an array of arrays

I attempted to ask a more complicated of this before but I couldn't explain it well so I am trying again with a simplified use case.
I will have an array of arrays like the following
var allData = [[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]]
I need to select 1 element from each array so that I get a unique set like [2,4,1,3,5] easy to do in this case as each array has all values. However this will rarely be the case. Instead I may have
var allData = [[1,2,4],[1,2],[1,2],[2,4,5],[1,2,3,5]]
In this case I couldn't pick 1 or 2 from the first array as that would prevent the 2nd and 3rd from having a unique combination. So something like [4,2,1,5,3] or [4,1,2,5,3] would be the only two possible answers for this combination.
The only way I see to do this is to just go through every combination but these will get fairly large so it doesn't seem reasonable as this happens real time. There are going to be at least 7 arrays, possibly 14 and distantly possible to have 31 so going through every combination would be fairly rough.
The 2nd part is if there is some way to "know" you have the best possible option. Say if there was some way I would know that having a single duplicate is my best case scenario. Even if I have to brute force it if I encounter a 1 duplication solution I would know to stop.
One easy way to get a very simple of this is to just subtract the number of possible choices from the number of elements but this is the correct answer in only the simplest of cases. Is there some type of library or anything to help solve these types of problems? It is a bit beyond my math abilities.
Here is something I have tried but it is too slow for larger sets and can fail. It works sometimes for the 2nd case I presented but only on luck
const allData = [[1,2,4],[1,2],[1,2],[2,4,5],[1,2,3,5]]
var selectedData = []
for (var i in allData){
console.log("length",allData[i].length)
var j = 0
while(j < allData[i].length){
console.log("chekcing",allData[i][j])
if (selectedData.includes(allData[i][j])){
console.log("removing item")
allData[i].splice(j,1)
}
else{j++}
}
var uniqueIds = Object.keys(allData[i])
console.log(uniqueIds)
var randId = Math.floor(Math.random() * uniqueIds.length)
console.log(randId)
selectedData.push(allData[i][randId])
console.log("selectedData",selectedData)
}
You can start with a fairly simple backtracking algorithm:
function pick(bins, n = 0, res = {}) {
if (n === bins.length) {
return res
}
for (let x of bins[n]) {
if (!res[x]) {
res[x] = n + 1
let found = pick(bins, n + 1, res)
if (found)
return found
res[x] = 0
}
}
}
//
let a = [[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 4]]
console.log(pick(a))
This returns a mapping item => bin index + 1, which is easy to convert back to an array if needed.
This should perform relatively well for N < 10, for more/larger bins you can think of some optimizations, for example, avoid the worst case scenario by sorting bins from smallest to longest, or, depending on the nature of elements, represent bins as bitmasks.
You could count all elements and take various comparison with same indices.
function x([...data]) {
while (data.some(Array.isArray)) {
const
counts = data.reduce((r, a, i) => {
if (Array.isArray(a)) a.forEach(v => (r[JSON.stringify(v)] = r[JSON.stringify(v)] || []).push(i));
return r;
}, {}),
entries = Object.entries(counts),
update = ([k, v]) => {
if (v.length === 1) {
data[v[0]] = JSON.parse(k);
return true;
}
};
if (entries.some(update)) continue;
const grouped = entries.reduce((r, [, a]) => {
const key = JSON.stringify(a);
r[key] = (r[key] || 0) + 1;
return r;
}, {});
Object.entries(grouped).forEach(([json, length]) => {
const indices = JSON.parse(json);
if (indices.length === length) {
let j = 0;
indices.forEach(i => data[i] = data[i][j++]);
return;
}
if (length === 1) {
const value = JSON.parse(entries.find(([_, a]) => JSON.stringify(a) === json)[0]);
indices.forEach(i => data[i] = data[i].filter(v => v !== value));
data[indices[0]] = value;
}
});
}
return data;
}
console.log(...x([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]));
console.log(...x([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5]]));
console.log(...x([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5], [6, 7, 8, 9], [6, 7, 8, 9], [6, 7, 8, 10], [6, 7, 8, 10], [6, 7, 8, 10]]));
Here is an implementation based around counting occurrences across the arrays.
It first creates a map indexed by value counting the number of inner arrays each value occurs in. It then sorts by inner array length to prioritize shorter arrays, and then iterates over each inner array, sorting by occurrence and selecting the first non-duplicate with the lowest count, or, if there are no unique values, the element with the lowest count.
const
occurrencesAcrossArrays = (arr) =>
arr
.reduce((a, _arr) => {
[...new Set(_arr)].forEach(n => {
a[n] = a[n] || 0;
a[n] += 1;
});
return a;
}, {}),
generateCombination = (arr) => {
const dist = occurrencesAcrossArrays(arr)
return arr
.sort((a, b) => a.length - b.length)
.reduce((a, _arr) => {
_arr.sort((a, b) => dist[a] - dist[b]);
let m = _arr.find(n => !a.includes(n));
if (m !== undefined) {
a.push(m);
} else {
a.push(_arr[0]);
}
return a;
}, []);
};
console.log(generateCombination([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]).toString());
console.log(generateCombination([[1, 2, 4], [1, 2], [1], [2, 4, 5], [1, 2, 3, 5]]).toString());
console.log(generateCombination([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5], [6, 7, 8, 9], [6, 7, 8, 9], [6, 7, 8, 10], [6, 7, 8, 10], [6, 7, 8, 10]]).toString());
Edit
In response to your comment – The situation seems to be emerging because the values all have the same occurrence count and are sequential.
This can be solved by keeping a running count of each value in the result array, and sorting each inner array by both by this running occurrence count as well as the original distribution count.This adds complexity to the sort, but allows you to simply access the first element in the array (the element with the lowest rate of occurrence in the result with the lowest occurrence count across all arrays).
const
occurrencesAcrossArrays = (arr) =>
arr
.reduce((a, _arr) => {
[...new Set(_arr)].forEach(n => {
a[n] = a[n] || 0;
a[n] += 1;
});
return a;
}, {}),
generateCombination = (arr) => {
const dist = occurrencesAcrossArrays(arr)
return arr
.sort((a, b) => a.length - b.length)
.reduce((acc, _arr) => {
_arr.sort((a, b) => (acc.occurrences[a] || 0) - (acc.occurrences[b] || 0) || dist[a] - dist[b]);
let m = _arr[0]
acc.occurrences[m] = acc.occurrences[m] || 0;
acc.occurrences[m] += 1;
acc.result.push(m);
return acc;
}, { result: [], occurrences: {} })
.result; // return the .result property of the accumulator
};
console.log(generateCombination([[2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6]]).toString());
// 2,3,4,5,6,2,3
console.log(generateCombination([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]).toString());
// 1,2,3,4,5
console.log(generateCombination([[1, 2, 4], [1, 2], [1], [2, 4, 5], [1, 2, 3, 5]]).toString());
// 1,2,4,5,3
console.log(generateCombination([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5], [6, 7, 8, 9], [6, 7, 8, 9], [6, 7, 8, 10], [6, 7, 8, 10], [6, 7, 8, 10]]).toString());
//1,2,4,5,3,9,6,10,7,8
console.log(generateCombination([[1], [2, 3,], [3, 4, 5], [3, 4, 5, 6], [2, 3, 4, 5, 6, 7]]).toString());
// 1,2,4,6,7
A note on .reduce()
If you're having trouble getting your head around .reduce() you can rewrite all the instances of it in this example using .forEach() and declaring accumulator variables outside of the loop. (This will not always be the case, depending on how you manipulate the accumulator value within a reduce() call).
Example below:
const occurrencesAcrossArrays = (arr) => {
const occurrences = {};
arr.forEach(_arr => {
[...new Set(_arr)].forEach(n => {
occurrences[n] = occurrences[n] || 0;
occurrences[n] += 1;
});
});
return occurrences;
};
const generateCombination = (arr) => {
const dist = occurrencesAcrossArrays(arr);
const result = [];
const occurrences = {};
arr.sort((a, b) => a.length - b.length);
arr.forEach(_arr => {
_arr.sort((a, b) => (occurrences[a] || 0) - (occurrences[b] || 0) || dist[a] - dist[b]);
let m = _arr[0]
occurrences[m] = occurrences[m] || 0;
occurrences[m] += 1;
result.push(m);
});
return result;
};
console.log(generateCombination([[2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6]]).toString());
// 2,3,4,5,6,2,3
console.log(generateCombination([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]).toString());
// 1,2,3,4,5
console.log(generateCombination([[1, 2, 4], [1, 2], [1], [2, 4, 5], [1, 2, 3, 5]]).toString());
// 1,2,4,5,3
console.log(generateCombination([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5], [6, 7, 8, 9], [6, 7, 8, 9], [6, 7, 8, 10], [6, 7, 8, 10], [6, 7, 8, 10]]).toString());
//1,2,4,5,3,9,6,10,7,8
console.log(generateCombination([[1], [2, 3,], [3, 4, 5], [3, 4, 5, 6], [2, 3, 4, 5, 6, 7]]).toString());
// 1,2,4,6,7
You could solve this problem using a MILP-model. Here is one implementation in MiniZinc (data has been extended to seven days):
int: Days = 7;
int: Items = 5;
set of int: DAY = 1..Days;
set of int: ITEM = 1..Items;
array[DAY, ITEM] of 0..1: A = % whether item k is allowed on day i
[| 1, 1, 0, 1, 0
| 1, 1, 0, 0, 0
| 1, 1, 0, 0, 0
| 0, 1, 0, 1, 1
| 1, 1, 0, 0, 0
| 0, 1, 0, 1, 1
| 1, 1, 1, 0, 1 |];
array[DAY, ITEM] of var 0..1: x; % 1 if item selected k on day i, otherwise 0
array[DAY, DAY, ITEM] of var 0..1: w; % 1 if item k selected on both day i and day j, otherwise 0
% exactly one item per day
constraint forall(i in DAY)
(sum(k in ITEM)(x[i, k]) = 1);
% linking variables x and w
constraint forall(i, j in DAY, k in ITEM where i < j)
(w[i, j, k] <= x[i, k] /\ w[i, j, k] <= x[j, k] /\ w[i, j, k] >= x[i, k] + x[j, k] - 1);
% try to minimize duplicates and if there are duplicates put them as far apart as possible
var int: obj = sum(i, j in DAY, k in ITEM where i < j)(((Days - (j - i))^2)*w[i, j, k]);
solve minimize obj;
output
["obj="] ++ [show(obj)] ++
["\nitem="] ++ [show([sum(k in ITEM)(k*x[i, k]) | i in DAY])];
Running gives:
obj=8
item=[2, 1, 5, 4, 3, 2, 1]
The following package looks promising for a JavaScript implementation: https://www.npmjs.com/package/javascript-lp-solver

Array spread values until matches length of another array length, to allow arrays to zip together

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}|`;
}

Categories