Iterating over an object of arrays and calculating average - javascript

I have the following data structure and need to get an average value for each of the columns for the items in the contenders object. I then need to turn this into an array of arrays. The first value needs to be the columns average (rounded) the second needs to be an incremented value starting at 0. e.g.
output = [[6, 0], [4, 1], [3, 2], [3, 3], [6, 4]];
Example structure:
input = {
categories: [
"Cat 1",
"Cat 2",
"Cat 3",
"Cat 4",
"Cat 5"
],
contenders: {
item1: [5, 3, 4, 4, 6],
item2: [6, 10, 4, 4, 6],
item3: [6, 3, 4, 9, 6],
item4: [8, 3, 5, 4, 6],
item5: [9, 3, 4, 4, 6],
item6: [10, 2, 7, 4, 6],
item7: [4, 3, 4, 4, 6],
item8: [1, 5, 4, 4, 6]
},
misc: [0, 3, 4, 4, 6]
};
I've created a function that can do the average for me:
function getAvg(data) {
return data.reduce(function (p, c) {
return p + c;
}) / data.length;
}
But can't quite work out how to iterate over the values for the items to get my result.

If I understood correctly and you want an average of each column, which would mean each first element in all the arrays in your items keys as example, then you would need to make an array of each column to use your average function. However, in doing so you can already calculate the average, here is a way:
var input = { categories: ["Cat 1", "Cat 2", "Cat 3", "Cat 4", "Cat 5"], contenders: { item1: [5, 3, 4, 4, 6], item2: [6, 10, 4, 4, 6], item3: [6, 3, 4, 9, 6], item4: [8, 3, 5, 4, 6], item5: [9, 3, 4, 4, 6], item6: [10, 2, 7, 4, 6], item7: [4, 3, 4, 4, 6], item8: [1, 5, 4, 4, 6] }, misc: [0, 3, 4, 4, 6] }
var output = []
var cols = input.contenders.item1.length // amount of columns (I assume all items have same amount of columns)
for(var i=0;i<cols;i++){
output[i] = [0,i] // start output with sum of 0 and index i
}
for(var key in input.contenders){
var arr = input.contenders[key]
for(var k = 0;k<cols;k++){
output[k][0]+=arr[k] // add the value of the array in the kth position to the kth output sum
}
}
for(var i=0;i<cols;i++){
output[i][0] = Math.round(output[i][0]/Object.keys(input.contenders).length) // now just divide by the amount of keys
}
console.log(output)

You could use a combination of Object.keys to get each item in the contenders, then use map to create a new array from the results:
function getAvg(data) {
return data.reduce(function (p, c) {
return p + c;
}) / data.length;
}
var input = {
categories: [
"Cat 1",
"Cat 2",
"Cat 3",
"Cat 4",
"Cat 5"
],
contenders: {
item1: [5, 3, 4, 4, 6],
item2: [6, 10, 4, 4, 6],
item3: [6, 3, 4, 9, 6],
item4: [8, 3, 5, 4, 6],
item5: [9, 3, 4, 4, 6],
item6: [10, 2, 7, 4, 6],
item7: [4, 3, 4, 4, 6],
item8: [1, 5, 4, 4, 6]
},
misc: [0, 3, 4, 4, 6]
};
console.log(Object.keys(input.contenders).map(function(key, index) {
return [getAvg(input.contenders[key]), index];
}));

You can use Object.keys, for getting the properties and then interate over the values with Array.prototype.forEach.
var input = { categories: ["Cat 1", "Cat 2", "Cat 3", "Cat 4", "Cat 5"], contenders: { item1: [5, 3, 4, 4, 6], item2: [6, 10, 4, 4, 6], item3: [6, 3, 4, 9, 6], item4: [8, 3, 5, 4, 6], item5: [9, 3, 4, 4, 6], item6: [10, 2, 7, 4, 6], item7: [4, 3, 4, 4, 6], item8: [1, 5, 4, 4, 6] }, misc: [0, 3, 4, 4, 6] },
output = [],
sum = [], count = [];
Object.keys(input.contenders).forEach(function (k) {
input.contenders[k].forEach(function (a, i) {
sum[i] = (sum[i] || 0) + a;
count[i] = (count[i] || 0) + 1;
});
});
output = sum.map(function (a, i) {
return [Math.round(sum[i] / count[i]), i]
});
document.write('<pre>' + JSON.stringify(output, 0, 4) + '</pre>');

You should totally go with Object.keys as suggested in other answers. However, for completeness or the unlikely case that compatibility gives you trouble:
You can build a string to refer to the arrays you're looking for, like so
for (var i=1;i<9;i++) { doSomething(input.contenders["item"+i]); }
This would require you to know the number of items and their names in advance.

Related

Get 3x3 blocks from 9x9 2d array as String

I am learning Javascript and currently found myself with the following problem. I need to get each individual 3x3 block of a 9x9 2d array as a string, separated by commas.
What I mean is, for example, let's say I have the following array:
var 2dArray = [
[5, 3, 5, 6, 7, 8, 9, 1, 2],
[6, 7, 1, 1, 9, 5, 3, 4, 8],
[1, 9, 2, 3, 4, 2, 5, 6, 7],
[8, 5, 9, 7, 6, 1, 4, 2, 3],
[4, 2, 6, 5, 5, 3, 7, 9, 1],
[7, 1, 3, 1, 2, 4, 8, 5, 6],
[9, 6, 1, 5, 3, 7, 2, 8, 4],
[2, 8, 7, 5, 1, 9, 6, 3, 5],
[3, 4, 5, 6, 8, 6, 1, 7, 9] ]
The result should be something like 535671192,678195342, 912348657, ... and so on, until the string is made of all the 3x3 blocks.
I thought that making nested for loops would be the best approach, but got confused along the way and I would appreciate your help.
Thank you.
You can use two loops to iterate over the positions of all the possible top left corners and another two loops to get all the elements in that square.
var arr = [
[5, 3, 5, 6, 7, 8, 9, 1, 2],
[6, 7, 1, 1, 9, 5, 3, 4, 8],
[1, 9, 2, 3, 4, 2, 5, 6, 7],
[8, 5, 9, 7, 6, 1, 4, 2, 3],
[4, 2, 6, 5, 5, 3, 7, 9, 1],
[7, 1, 3, 1, 2, 4, 8, 5, 6],
[9, 6, 1, 5, 3, 7, 2, 8, 4],
[2, 8, 7, 5, 1, 9, 6, 3, 5],
[3, 4, 5, 6, 8, 6, 1, 7, 9]
];
let res = [];
for (let i = 0; i < 9; i += 3) {
for (let j = 0; j < 9; j += 3) {
let curr = "";
for (let k = 0; k < 3; k++) {
for (let l = 0; l < 3; l++) {
curr += arr[i + k][j + l];
}
}
res.push(curr);
}
}
console.log(res);
The issue can also be solved by loops but it's good practice to solve it using the Array.map() function.
let arrayRow = "";
const resultArray = Array2d.map((element, index) => {
return arrayRow + element.map((element, i) => {
return element.toString();
});
});
you should try like this too.
var arr = [
[5, 3, 5, 6, 7, 8, 9, 1, 2],
[6, 7, 1, 1, 9, 5, 3, 4, 8],
[1, 9, 2, 3, 4, 2, 5, 6, 7],
[8, 5, 9, 7, 6, 1, 4, 2, 3],
[4, 2, 6, 5, 5, 3, 7, 9, 1],
[7, 1, 3, 1, 2, 4, 8, 5, 6],
[9, 6, 1, 5, 3, 7, 2, 8, 4],
[2, 8, 7, 5, 1, 9, 6, 3, 5],
[3, 4, 5, 6, 8, 6, 1, 7, 9]
];
let res = [];
let i = 0,j = 0;
let justOneStr = "";
while(i < 9 && j < 9){
justOneStr += arr[i][j];
if(i % 3 == 2 && j % 3 == 2){
res.push(justOneStr);
justOneStr="";
}
if((j % 9 == 8 || j % 3 == 2) && i % 3 != 2){
j -= 3;
i += 1;
}
else if(j % 9 == 8 && i % 3 == 2){
j -= 9;
i += 1;
}
else if(i % 3 == 2 && j % 3 == 2){
i -= 2;
}
j++;
}
console.log(res);
Here's a fairly straightforward nested reduce() that returns an array of matrices of the specified size. It touches each element once, and uses the coordinates in the passed matrix to determine which sub-matrix and sub-matrix row to accumulate into.
const
matrix = [
[5, 3, 5, 6, 7, 8, 9, 1, 2],
[6, 7, 1, 1, 9, 5, 3, 4, 8],
[1, 9, 2, 3, 4, 2, 5, 6, 7],
[8, 5, 9, 7, 6, 1, 4, 2, 3],
[4, 2, 6, 5, 5, 3, 7, 9, 1],
[7, 1, 3, 1, 2, 4, 8, 5, 6],
[9, 6, 1, 5, 3, 7, 2, 8, 4],
[2, 8, 7, 5, 1, 9, 6, 3, 5],
[3, 4, 5, 6, 8, 6, 1, 7, 9]],
subdivide = (matrix, width, height) => {
return matrix.reduce((acc, row, i) => {
row.reduce((_acc, x, j, row) => {
const grid = Math.floor(j / width) + Math.floor(i / height) * (Math.ceil(row.length / width));
const gridRow = i % height;
_acc[grid] ??= [];
(_acc[grid][gridRow] ??= []).push(x);
return _acc;
}, acc)
return acc;
}, [])
},
subdividedmatrix = subdivide(matrix, 3, 3);
// log submatrices
subdividedmatrix.forEach(m => console.log(JSON.stringify(m)));
// map to strings
console.log(subdividedmatrix.map(submatrix => submatrix.flat().join('')))
.as-console-wrapper { max-height: 100% !important; top: 0; }

remove duplicate elements in proceeding arrays inside array of arrays

We have an array of arrays like this:
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
There may be duplicate elements in each array and that's fine.
But I'm after a proper solution to remove duplicate elements in each set comparing to lower sets!
So as we have a 0 in the first array and the last array, we should consider the 0 in last one a duplication and remove it...
the desired result would be:
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[12],
[14]
It's a confusing issue for me please help...
You could collect the values in an object with index as value, and filter for values who are at the same index.
const
arrays = [[0, 1, 2, 3, 4, 4, 4, 4], [5, 6, 7, 8, 9, 10, 11, 11], [2, 7, 10, 12], [0, 7, 10, 14]],
seen = {},
result = arrays.map((array, i) => array.filter(v => (seen[v] ??= i) === i));
result.forEach(a => console.log(...a));
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[4, 4, 5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
]
let filtered = arrays.map((row, i) => {
// concat all previous arrays
let prev = [].concat(...arrays.slice(0, i))
// filter out duplicates from prev arrays
return row.filter(r => !prev.includes(r))
})
console.log(filtered)
We can do this using Array#reduce and maintain a seen Set, which will have all visited numbers from each array.
Once you iterate over an array you push all visited elements in the seen Set, then push a new array filtered by the elements not in the seen Set:
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
const removeDupsInSibling = (arr) => {
let seen = new Set();
return arr.reduce((acc, a)=> {
const f = a.filter(v => !seen.has(v));
seen = new Set([...seen, ...a]);
acc.push(f);
return acc;
}, []);
}
console.log(removeDupsInSibling(arrays));
There are plenty of inefficient ways to do this, but if you want to do this in O(n), then we can make the observation that what we want to know is "which array a number is in". If we know that, we can run our algorithm in O(n):
for every element e in array at index i:
if index(e) == i:
this is fine
if index(e) < i:
remove this e
So let's just do literally that: we allocate an object to act as our lookup, and then we run through all elements:
const lookup = {};
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
const reduced = arrays.map((array, index) => {
// run through the elements in reverse, so that we can
// safely remove bad elements without affecting the loop:
for(let i=array.length-1; i>=0; i--) {
let value = array[i];
let knownIndex = (lookup[value] ??= index);
if (knownIndex < index) {
// removing from "somewhere" in the array
// uses the splice function:
array.splice(i,1);
}
}
return array;
});
console.log(reduced);
For an alternative, where the loop+splice is taken care of using filter, see Nina's answer.
Simple, clean and high performance solution:
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
const duplicates = {};
const answer = arrays.map( (array, level) => {
return array.filter( el => {
if ( duplicates[el] < level ) {
// return nothing; fine
} else {
duplicates[el] = level;
return el
}
})
});
console.log(JSON.stringify(answer))
here is on-liner and less-readable form:
const d = {}, arrays = [ [0, 1, 2, 3, 4, 4, 4, 4], [5, 6, 7, 8, 9, 10, 11, 11], [2, 7, 10, 12], [0, 7, 10, 14]];
const answer = arrays.map((a,l)=> a.filter(el=> d[el]<l ? 0 : (d[el]=l,el)));
console.log(JSON.stringify(answer))
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14],
];
const output = arrays.reduce(
({ output, set }, current, i) => {
output[i] = current.filter((num) => !set.has(num));
[...new Set(output[i])].forEach((num) => set.add(num));
return { output, set };
},
{ output: [], set: new Set() }
).output;
console.log(output);
Gets the exact output you want:
[
[
0, 1, 2, 3,
4, 4, 4, 4
],
[
5, 6, 7, 8,
9, 10, 11, 11
],
[ 12 ],
[ 14 ]
]

Find if few array items exists in any one of many arrays?

Have these 2 Javascript arrays
// The player which has a "winning" combination (1, 5, 9)
player1Cells = [ 5, 9, 1, 6]
// Player which doesnt have a "winning" combination
player2Cells = [ 2, 1, 4, 8]
A player has a "winning" combination if 3 of their numbers match one of the arrays in this array:
winningCombinations = [
[1, 2, 3], [4, 5, 6], [7, 8, 9],
[1, 4, 7], [2, 5, 8], [3, 6, 9],
[1, 5, 9], [3, 5, 7]
];
In the example, player1Cells has a winning combination - 1, 5, 9. The other player doesn't
I figured there is a way to loop through the winningCombinations in some way, and compare it against the player compare but I don't know best approach to comparing these in an efficient way.
You can use some method on the winning array and then filter method to check if player has more than 3 matches as current array in some loop.
const w = [[1, 2, 3], [4, 5, 6], [7, 8, 9],[1, 4, 7], [2, 5, 8], [3, 6, 9],[1, 5, 9], [3, 5, 7]];
const player1Cells = [ 5, 9, 1, 6];
const player2Cells = [ 2, 1, 4, 8];
function check(arr, p) {
return arr.some(a => {
const match = p.filter(e => a.includes(e));
return match.length >= 3;
})
}
console.log(check(w, player1Cells));
console.log(check(w, player2Cells));
You may use:
Array.prototype.some()
Array.prototype.every()
Array.prototype.includes()
Working Example:
let player1Cells = [5, 9, 1, 6];
let player2Cells = [2, 1, 4, 8];
let winningCombinations = [
[1, 2, 3], [4, 5, 6], [7, 8, 9],
[1, 4, 7], [2, 5, 8], [3, 6, 9],
[1, 5, 9], [3, 5, 7]
];
let checker = function(w, p) {
return w.some(a => a.every(v => p.includes(v)));
}
console.log(checker(winningCombinations, player1Cells));
console.log(checker(winningCombinations, player2Cells));

Algorithm in javascript or python that takes a list of numbers and "groups" them into approximately similar size? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I am looking for an algorithm that takes 2 inputs (#1: a number, #2: an array of numbers [that may be duplicated]):
Output should be the desired groups (with the individual numbers from the original list in them). The groups should be as close in length/size. Note that if a number is in one group, all other items in the list with the same number would be put in that same group. (e.g. so you would not have the number 3 say in multiple output groups).
NOTE that elements in returned groups MUST not overlap in their range of numbers
So you cannot have two output groups like this [[1,1,1,2,2,4,4,4], [3,3,5,5,6,7,8,16]] because the range of numbers in each subgroup is [1-4] and [3-16], which have an overlap. You can only have groups like [1-3][4-16] (note there is no overlap in this case).
Sample #1 Input/Output
1) 3 Desired Groups
2) List: [1,1,2,2,3,3]
Output:
[[1,1],[2,2],[3,3]]
Sample #2 Input/Output
Input 2 desired groups/subarrays to be output, and the following list of numbers:
[1,1,1,2,2,3,3,4,4,4,5,5,6,7,8,16]
Output are two subarrays that contain the following:
[1,1,1,2,2,3,3,4,4,4]
[5,5,6,7,8,16]
Note #1: Output two subarrays/groups of [[1,1,1,2,2,3,3] [4,4,4,5,5,6,7,8,16]] would also be valid given that there is no way to output equal groups.
Note #2: While subgroups of: [[1,1,1,2,2,4,4,4], [3,3,5,5,6,7,8,16]] would be equal in length, it violates the rule of "no overlapping ranges between returned subgroups", meaning the range of numbers in each subgroup cannot overlap with each other. (e.g. In this note, the ranges of the subgroups returned are [1-4] and [3-16], if you took a ruler and drew a line from the numbers 1-4 and draw another line from 3-16, you would see that 3-4 would have overlapping lines, which is not what we want in this case).
In the sample output of this example, the ranges of the two groups are [1-4] and [5-16], which if you took a ruler/tape measure and drew lines where the numbers are, the lines would not overlap with each other, which is what we want.
Sample #3 Input/Output
Input 3 desired groups/subarrays to be output, and the following list of numbers:
[1,1,1,2,2,3,3,4,4,4,5,5,6,7,8,16]
Output:
[1,1,1,2,2]
[3,3,4,4,4]
[5,5,6,7,8,16]
Note in this case, since there is no way to achieve the same # of items, algorithm outputs the best it can do where one group is only 1 bigger than the others.
Sample #4 Input/Output
Input: "4 desired groups", and the following list:
[1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8, 16]
Possible Output:
[1,1,1,2,2]
[3,3,4,4,4]
[5,5,6]
[7,8,16]
Note: Preferrably, the output should contain more than 1 unique number when possible. While an output of [[1, 1, 1], [2, 2, 3, 3], [4, 4, 4, 5, 5], [6, 7, 8, 16]] does provide approximately similar groupings, it is preferred that there is more than 1 unique number in a single subgroup. In this "Note", 1 is the only number in group 1, while in the sample output of this example, group 1 contains unique numbers 1 and 2, which is preferred.
What is a good way to perform this?
Grouping common numbers then using a recursive function
My last solution didn't give the right results, so this is a different algorithm. It follows:
group the list into sub-lists of common numbers
check if this list has the required number of groups (sub-lists)
if it does then add it to a list of possible solutions and end
else, create a list of all the different pairs of common numbers in the current list merged together
for each list in (4), go back to (2)
If you followed the algorithm, you can see that it will work by branching off down the differently merged lists until it reaches one of the required length where that branch will end. This is a perfect opportunity to write a recursive function.
But before that, we needed a small method for grouping common numbers from the original list. To do this, a small for-loop goes through each number and check if it belongs as part of the previous common numbers. If it does, add it to them, or else it creates its own common numbers.
This may look something like:
l = [1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6]
l.sort()
groups = []
for i in l:
if groups and i in groups[-1]:
groups[-1].append(i)
else:
groups.append([i])
now groups is:
[[1], [2, 2], [3, 3, 3], [4, 4], [5, 5], [6]]
so we are ready for the recursive funtion:
def merge(grps, numGrps):
global solutions
if len(grps) <= numGrps:
solutions.append(grps)
return
merges = [grps[:i] + [grps[i] + grps[i+1]] + grps[i+2:] for i in range(len(grps)-1)]
for m in merges:
merge(m, numGrps)
the function is self-explanatory, but the list-comprehension is the important part which controls the branching:
[grps[:i] + [grps[i] + grps[i+1]] + grps[i+2:] for i in range(len(grps)-1)]
It is essentially saying: for each number up to the length of the current list, take the common number groups before it grps[:i], add them onto its common numbers grps[i] merged with the next common numbers grps[i+1] and then add that onto the rest of the common numbers grps[i+2:].
From there, we just set the function going with each of the different merged common number combinations along with its target number of groups.
The final code
The final code put together would be:
l = [1,2,2,3,3,3,4,4,5,5,6]
l.sort()
groups = []
for i in l:
if groups and i in groups[-1]:
groups[-1].append(i)
else:
groups.append([i])
print("original list:")
print(l, "\n")
print("common numbers grouping:")
print(groups)
print()
def merge(grps, numGrps):
global solutions
if len(grps) <= numGrps:
solutions.append(grps)
return
merges = [grps[:i] + [grps[i] + grps[i+1]] + grps[i+2:] for i in range(len(grps)-1)]
for m in merges:
merge(m, numGrps)
solutions = []
merge(groups, 3)
print("possible solutions:\n")
for s in solutions:
print(s)
which outputs:
original list:
[1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6]
common numbers grouping:
[[1], [2, 2], [3, 3, 3], [4, 4], [5, 5], [6]]
possible solutions:
[[1, 2, 2, 3, 3, 3, 4, 4], [5, 5], [6]]
[[1, 2, 2, 3, 3, 3], [4, 4, 5, 5], [6]]
[[1, 2, 2, 3, 3, 3], [4, 4], [5, 5, 6]]
[[1, 2, 2, 3, 3, 3, 4, 4], [5, 5], [6]]
[[1, 2, 2], [3, 3, 3, 4, 4, 5, 5], [6]]
[[1, 2, 2], [3, 3, 3, 4, 4], [5, 5, 6]]
[[1, 2, 2, 3, 3, 3], [4, 4, 5, 5], [6]]
[[1, 2, 2], [3, 3, 3, 4, 4, 5, 5], [6]]
[[1, 2, 2], [3, 3, 3], [4, 4, 5, 5, 6]]
[[1, 2, 2, 3, 3, 3], [4, 4], [5, 5, 6]]
[[1, 2, 2], [3, 3, 3, 4, 4], [5, 5, 6]]
[[1, 2, 2], [3, 3, 3], [4, 4, 5, 5, 6]]
[[1, 2, 2, 3, 3, 3, 4, 4], [5, 5], [6]]
[[1, 2, 2, 3, 3, 3], [4, 4, 5, 5], [6]]
[[1, 2, 2, 3, 3, 3], [4, 4], [5, 5, 6]]
[[1, 2, 2, 3, 3, 3, 4, 4], [5, 5], [6]]
[[1], [2, 2, 3, 3, 3, 4, 4, 5, 5], [6]]
[[1], [2, 2, 3, 3, 3, 4, 4], [5, 5, 6]]
[[1, 2, 2, 3, 3, 3], [4, 4, 5, 5], [6]]
[[1], [2, 2, 3, 3, 3, 4, 4, 5, 5], [6]]
[[1], [2, 2, 3, 3, 3], [4, 4, 5, 5, 6]]
[[1, 2, 2, 3, 3, 3], [4, 4], [5, 5, 6]]
[[1], [2, 2, 3, 3, 3, 4, 4], [5, 5, 6]]
[[1], [2, 2, 3, 3, 3], [4, 4, 5, 5, 6]]
[[1, 2, 2, 3, 3, 3, 4, 4], [5, 5], [6]]
[[1, 2, 2], [3, 3, 3, 4, 4, 5, 5], [6]]
[[1, 2, 2], [3, 3, 3, 4, 4], [5, 5, 6]]
[[1, 2, 2, 3, 3, 3, 4, 4], [5, 5], [6]]
[[1], [2, 2, 3, 3, 3, 4, 4, 5, 5], [6]]
[[1], [2, 2, 3, 3, 3, 4, 4], [5, 5, 6]]
[[1, 2, 2], [3, 3, 3, 4, 4, 5, 5], [6]]
[[1], [2, 2, 3, 3, 3, 4, 4, 5, 5], [6]]
[[1], [2, 2], [3, 3, 3, 4, 4, 5, 5, 6]]
[[1, 2, 2], [3, 3, 3, 4, 4], [5, 5, 6]]
[[1], [2, 2, 3, 3, 3, 4, 4], [5, 5, 6]]
[[1], [2, 2], [3, 3, 3, 4, 4, 5, 5, 6]]
[[1, 2, 2, 3, 3, 3], [4, 4, 5, 5], [6]]
[[1, 2, 2], [3, 3, 3, 4, 4, 5, 5], [6]]
[[1, 2, 2], [3, 3, 3], [4, 4, 5, 5, 6]]
[[1, 2, 2, 3, 3, 3], [4, 4, 5, 5], [6]]
[[1], [2, 2, 3, 3, 3, 4, 4, 5, 5], [6]]
[[1], [2, 2, 3, 3, 3], [4, 4, 5, 5, 6]]
[[1, 2, 2], [3, 3, 3, 4, 4, 5, 5], [6]]
[[1], [2, 2, 3, 3, 3, 4, 4, 5, 5], [6]]
[[1], [2, 2], [3, 3, 3, 4, 4, 5, 5, 6]]
[[1, 2, 2], [3, 3, 3], [4, 4, 5, 5, 6]]
[[1], [2, 2, 3, 3, 3], [4, 4, 5, 5, 6]]
[[1], [2, 2], [3, 3, 3, 4, 4, 5, 5, 6]]
[[1, 2, 2, 3, 3, 3], [4, 4], [5, 5, 6]]
[[1, 2, 2], [3, 3, 3, 4, 4], [5, 5, 6]]
[[1, 2, 2], [3, 3, 3], [4, 4, 5, 5, 6]]
[[1, 2, 2, 3, 3, 3], [4, 4], [5, 5, 6]]
[[1], [2, 2, 3, 3, 3, 4, 4], [5, 5, 6]]
[[1], [2, 2, 3, 3, 3], [4, 4, 5, 5, 6]]
[[1, 2, 2], [3, 3, 3, 4, 4], [5, 5, 6]]
[[1], [2, 2, 3, 3, 3, 4, 4], [5, 5, 6]]
[[1], [2, 2], [3, 3, 3, 4, 4, 5, 5, 6]]
[[1, 2, 2], [3, 3, 3], [4, 4, 5, 5, 6]]
[[1], [2, 2, 3, 3, 3], [4, 4, 5, 5, 6]]
[[1], [2, 2], [3, 3, 3, 4, 4, 5, 5, 6]]
Sorting
Now that you have ALL the possible solutions, you can sort them any way you want. So for example, if you wanted to select the one with the most even spread in length in the group, you would calculate each groups length and the difference between smallest and biggest lengths would be the score on which you rank them.
Even though there are other ways of sorting them, as we have seen in the comment section, the one described above seems to be what most people want so here it is:
smallestDiff = 9999
for s in solutions:
lenDiff = max([len(a) - len(b) for a in s for b in s])
if lenDiff < smallestDiff:
smallestDiff = lenDiff
sol = s
and for my example with list:
[1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6]
we have the result:
[[1, 2, 2], [3, 3, 3, 4, 4], [5, 5, 6]]
which I would consider to be the best solution in this case. And finally, to check with the examples given in the question:
Sample 1
groups = 3
l = [1, 1, 2, 2, 3, 3]
gives output of:
[[1, 1], [2, 2], [3, 3]]
Sample 2
groups = 2
l = [1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8, 16]
gives output of:
[[1, 1, 1, 2, 2, 3, 3], [4, 4, 4, 5, 5, 6, 7, 8, 16]]
So it is clear this algorithm works and I hope this helps.
You can use .map() and .filter() to create an array of arrays containing the same elements, collect values of arrays where there is single element to a single array, if they exist, .splice() expected number of arrays (groups) from collected array, reinsert original single values into to groups, return result
let arr = [1,1,1,2,2,3,3,4,4,4,5,5,6,7,8,16]
const group = (arr, groups) => {
// if input array `.length` is `0`
// or `groups` is less than 2, throw `TypeError`
if (!arr.length) {
throw new TypeError("input array does not have valid `.length`")
}
// declare resulting array `res`,
// `singles` array to group values of arrays having `.length` of `1`
// to single array
// `s` array of groups of same element from input array
// `len` whole number to `.splice()` `s` into `groups` by
// `t` array of single elements from `s`, if they exist
let [res, singles] = [[], []];
const [s
, len = Math.floor(s.length / groups -1)
, t = s.filter(el => el.length === 1)] = [
[...new Set(arr)].map(el => arr.filter(n => n === el))
];
if (t.length) {
for (const g of t) {
// collect our single element arrays to `singles` array
singles = [...singles, ...g];
// remove singles element arrays from `s`
s.splice(s.indexOf(g), 1);
}
// `.push()` `singles` to `s`
s.push(singles);
}
do {
// `.splice()` our groups
const curr = s.splice(0, len);
// `.push()` current group to `res`
if (res.length < groups) {
res.push([])
};
// push elements of arrays to current array in `res`
for (const g of curr) {
res[res.length - 1] = [...res[res.length - 1], ...g]
}
} while (s.length);
// return result `res` array
return res;
}
let g2 = group(arr, 2);
let g3 = group(arr, 3);
let g4 = group(arr, 4);
console.log(g2, g3, g4);
We can use itertools.groupby to group the duplicated items together, and then use a simple "greedy" algorithm to assign each group to a sublist. If we have any leftover items at the end of the main loop, we put them in a new sublist unless we've already reached the desired number of sublists, in which case we simply add the leftovers to the last existing sublist.
The result isn't perfect: depending on the data, it may not even create enough sublists, but with well-behaved data the results are reasonable, IMHO. ;)
from itertools import groupby
def equal_groups(seq, num):
grouplen = len(seq) // num
result, current = [], []
for _, g in groupby(seq):
g = list(g)
# If this group is too big to go in current, flush current
if current and len(current) + len(g) > grouplen:
result.append(current)
current = []
current.extend(g)
# Deal with any leftovers
if current:
if len(result) < num:
result.append(current)
else:
result[-1].extend(current)
return result
# Test
data = [1,1,1,2,2,3,3,4,4,4,5,5,6,7,8,16]
for i in range(1, 8):
print(i, equal_groups(data, i))
output
1 [[1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8, 16]]
2 [[1, 1, 1, 2, 2, 3, 3], [4, 4, 4, 5, 5, 6, 7, 8, 16]]
3 [[1, 1, 1, 2, 2], [3, 3, 4, 4, 4], [5, 5, 6, 7, 8, 16]]
4 [[1, 1, 1], [2, 2, 3, 3], [4, 4, 4], [5, 5, 6, 7, 8, 16]]
5 [[1, 1, 1], [2, 2], [3, 3], [4, 4, 4], [5, 5, 6, 7, 8, 16]]
6 [[1, 1, 1], [2, 2], [3, 3], [4, 4, 4], [5, 5], [6, 7, 8, 16]]
7 [[1, 1, 1], [2, 2], [3, 3], [4, 4, 4], [5, 5], [6, 7], [8, 16]]

Add specific values of multidimensional array together Javascript

Okay, so I have a multidimensional array that itself contains 9 arrays. Each of these nested arrays contains 10 numeric values. For sake of simplicity, let's say it all looks like this:
var MyArray = [
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10]
]
I am trying to write a function that will take the first index of each nested array (in this case, all 1's) and add them together, pushing this value either to an array or an object. Then, I need this function to continue on, adding all the values of the next index, and the next, and so on and so forth. In the end, I should have an array of 10 values (or an object works here as well). The values would be:
1+1+1+1+1+1+1+1+1,
2+2+2+2+2+2+2+2+2,
3+3+3+3+3+3+3+3+3...
...and so on so forth, so that the actual values of the new array would be this:
[9, 18, 27, 36, 45, 54, 63, 72, 81]
The catch here is that I need this to by flexible/dynamic, so that it will work in case MyArray has only 6 arrays, or maybe the nested arrays have only 4 values each. It should work with any amount of nested arrays, each with their own amount of values (though each nested array will contain the SAME amount of values as one another!).
What would be the best way to accomplish this via JavaScript and/or jQuery? Note that I could also have the values output to an object, in this fashion:
{1:9, 2:18, 3:27, 4:36, 5:45, 6:54, 7:63, 8:72, 9:81}
I tried using similar code to this from another StackOverflow thread to get an object, but it is returning
{1:NaN, 2:NaN, 3:NaN, etc.}
That thread can be found here:
Javascript Multidimensional Array: Add Values
I'm using the "underscore" method and the jQuery $.each part of it provided by Otto.
Anyone able to help here??
Something like this
var myData = [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
];
var summed = [];
myData[0].forEach(function (arr, index) {
var sum = myData.reduce(function (a, b) {
return a + b[index];
}, 0);
summed.push(sum);
});
console.log(summed);
On jsfiddle
Here is another solution:
var MyArray = [
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10]
]
var results= [];
MyArray.map(function(a){
for(var i=0;i<a.length;i++){
if(results.length === a.length){
results[i] = results[i] + a[i];
}else{
results.push(a[i]);
}
}
});
http://jsfiddle.net/uMPAA/
A simple array solution would be the following :
var results= [];
for (var i=0;i<MyArray.length;i++) {
for(var j=0; j<MyArray[i].length; j++) {
if(results[j] == undefined) { results[j] = 0; }
results[j] = results[j]+data[i][j];
}
}
Note the if(results[j]==undefined) line -- this is probably what you didn't do. If you omit that, you get NaN on all lines, since you're adding an undefined value to a number.
Another approach to sum columns in multi-dimensional arrays (based on Lodash 4).
var arrays = [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
];
function sum_col(arrays) {
return _.map(_.unzip(arrays), _.sum);
}
console.log(sum_col(arrays));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Categories

Resources