Related
Let's say I have a 2D array filled with a lot of blank spaces, but there are smaller isolated 2D arrays contained between these blanks. For example:
var aVO = [
["col1", "col2", "col3", "col4", "col5", "col6", "col7", "col8", "col9"],
["", "", "", "", "", "", "", "", ""],
[1, 2, 3, "", "", "", "", "", ""],
[4, 5, 6, "", "", "a", "b", "", ""],
[7, 8, 9, "", "", "c", "d", "", 1],
["", "", "", "", "", "", "", "", 2],
["", "", "z", "y", "", "", "", "", 3],
["", "x", "w", "v", "", 7, 7, 7, ""],
["", "", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", "", ""],
["", "A1", "B1", "", "", "", "", "", ""],
["", "A2", "B2", "C2", "", "", "HELLO", "", ""]
]
I'm interested in converting this into eight 2D arrays:
[["col1", "col2", "col3", "col4", "col5", "col6", "col7", "col8", "col9"]]
[[1,2,3],[4,5,6],[7,8,9]]
[["a","b"],["c","d"]]
[[1],[2],[3]]
[["", "z","y"],["x","w", "v"]]
[[7,7,7]]
[["A1","B1",""],["A2","B2","C2"]]
[["HELLO"]]
What's the best approach to extract these smaller 2D arrays? I was thinking about iterating row-by-row but it's hard to visualize how to elegantly extract data like [["", "z","y"],["x","w","v"]] (note how "x" isn't directly below "z", and therefore the resulting 2D array needs to reflect that shift). Thanks for any help!
Here's an approach using Set and Map instances to keep track of groups of cells:
Create cells out of the 2d array
Create an empty Map in which we will store for each cell with a value to which group it belongs
Loop over each cell
If a cell is empty, go to the next one
If a cell has a value
Create a group for it. The group marks a top left and bottom right position and keeps track of a set of cells that belong to it.
Check which adjacent cells already belong to a group
Merge the newly created group with all of the groups found for adjacent cells
Collect all unique groups from the Map
For each unique group, slice out the part between its top left and bottom right corner from the initial grid
const Cell = memo(
(r, c) => ({ r, c }),
([r, c]) => `${r}_${c}`
);
Cell.neighbors = ({ r, c }) => [
Cell(r, c + 1), // Right
Cell(r + 1, c), // Down
Cell(r, c - 1), // Left
Cell(r - 1, c), // Up
];
// Create a single-cell group
const Group = (cell) => ({
minR: cell.r,
maxR: cell.r + 1,
minC: cell.c,
maxC: cell.c + 1,
cells: new Set([cell])
});
// Merge two groups into a new one
Group.merge = (g1, g2) => ({
minR: Math.min(g1.minR, g2.minR),
maxR: Math.max(g1.maxR, g2.maxR),
minC: Math.min(g1.minC, g2.minC),
maxC: Math.max(g1.maxC, g2.maxC),
cells: new Set([ ...g1.cells, ...g2.cells ])
});
// Take a grid and slice out the part covered by a group
Group.extractFromGrid = grid => ({ minR, maxR, minC, maxC }) => grid
.slice(minR, maxR)
.map(row => row.slice(minC, maxC));
// Create all cells with their values
const grid = getData();
const allCells = grid.flatMap(
(row, ri) => row.map(
(value, ci) => Cell(ri, ci)
)
);
const groupPerCell = new Map();
allCells.forEach(current => {
const inIsland = grid[current.r][current.c] !== "";
if (inIsland) {
const newGroup = Cell
.neighbors(current)
.filter(c => groupPerCell.has(c))
.map(c => groupPerCell.get(c))
.reduce(Group.merge, Group(current));
// Store a reference to the group for each member
newGroup.cells.forEach(c => {
groupPerCell.set(c, newGroup);
});
}
});
const allGroups = [...new Set(groupPerCell.values())];
const allValues = allGroups.map(Group.extractFromGrid(grid));
console.log(allValues);
function memo(f, hash) {
const cache = {};
return (...args) => {
const k = hash(args);
if (!cache[k]) cache[k] = f(...args);
return cache[k];
}
}
function getData() { return [
["col1", "col2", "col3", "col4", "col5", "col6", "col7", "col8", "col9"],
["", "", "", "", "", "", "", "", ""],
[1, 2, 3, "", "", "", "", "", ""],
[4, 5, 6, "", "", "a", "b", "", ""],
[7, 8, 9, "", "", "c", "d", "", 1],
["", "", "", "", "", "", "", "", 2],
["", "", "z", "y", "", "", "", "", 3],
["", "x", "w", "v", "", 7, 7, 7, ""],
["", "", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", "", ""],
["", "A1", "B1", "", "", "", "", "", ""],
["", "A2", "B2", "C2", "", "", "HELLO", "", ""]
]; }
I'm trying to take this array of arrays,
const parsedCsvData = [
[
"Passage",
"Percentage of",
"constraint2",
"1",
"2",
"",
"",
"",
"",
"",
"",
""
],
[
"",
"",
"",
"",
"",
"",
"",
"",
"Avg A Param",
"",
"0.3",
"0.9"
],
[
"Item",
"Include",
"constraint1",
"1",
"4",
"",
"",
"",
"",
"",
"",
""
],
[
"",
"",
"",
"",
"",
"",
"",
"",
"Item Identifier",
"I105_15201",
"",
""
]
]
and populate this object with their values
const template = {
"attributeName": "",
"constraintType": "",
"objectType": "",
"filters": [
{
"objectType": "",
"attributeName": "",
"values": "",
"valueLowerBound": "",
"valueUpperBound": ""
}
],
"upperBound": "",
"description": "",
"lowerBound": ""
}
each array from parsedCsvData was a row in a CSV file I parsed. Each value in the array of arrays is indexed based off another array
const csvHeaders = [
"objectType",
"constraintType",
"description",
"lowerBound",
"upperBound",
"attributeName",
"referenceValue",
"scope",
"filters",
"values",
"valueLowerBound",
"valueUpperBound"
]
For example, in the first sub array of parsedCsvData Passage is at index 0 and the value for objectType in csvHeaders. Similarly, description is at index 2 and is constraint1 and constraint2 respectively.
Additionally, if there exists a filters value at index 8 in the csv a new row was created. After parsing, this created a new sub array for each filter value. However, each filters value and the following three, values, valueLowerBound and valueUpperBound belong in the same template object as a subarray under the filters key.
Here is an example of the output I am trying to accomplish given what I posted above. Never mind the empty key/value pairs.
[{
"constraintType": "Percentage of",
"objectType": "Passage",
"filters": [
{
"attributeName": "Avg A Param",
"values": "",
"valueLowerBound": "0.3",
"valueUpperBound": "0.9"
} // there may be multiple filter objects as part of this array
],
"upperBound": "2",
"description": "constraint2",
"lowerBound": "1"
},
{
"constraintType": "Include",
"objectType": "Item",
"filters": [
{
"attributeName": "Item Identifier",
"values": "I105_15201",
"valueLowerBound": "",
"valueUpperBound": ""
}
],
"upperBound": "4",
"description": "constraint1",
"lowerBound": "1"
}
]
I've tried mapping the csvHeaders array, attempting to use their indices and then do a map of the the parsedCsvData values and assign them to the template object, but can't seem to get it to work. Any help would be much appreciated.
An approach by checking the first element of the nested arra for either assign a new object or assign a new nested object.
const
parsedCsvData = [["Passage", "Percentage of", "constraint2", "1", "2", "", "", "", "", "", "", ""], ["", "", "", "", "", "", "", "", "Avg A Param", "", "0.3", "0.9"], ["Item", "Include", "constraint1", "1", "4", "", "", "", "", "", "", ""], ["", "", "", "", "", "", "", "", "Item Identifier", "I105_15201", "", ""]],
template = ["objectType", "constraintType", "description", "lowerBound", "upperBound", "xxxx", "referenceValue", "scope", "attributeName", "values", "valueLowerBound", "valueUpperBound"],
result = parsedCsvData.reduce((r, a) => {
const
assign = (object, target, value) => {
const
keys = target.split('.'),
last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value;
};
if (a[0]) {
r.push(a.reduce((t, v, i) => {
if (v) assign(t, template[i], v);
return t;
}, {}));
} else {
r[r.length - 1].filters = r[r.length - 1].filters || [];
r[r.length - 1].filters.push(a.reduce((t, v, i) => {
if (v) assign(t, template[i], v);
return t;
}, {}));
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
My console shows me the total of all the scores but I would like it to print 32 and 35 instead of 65. The end result is I want to push each total into an array.
var peopleArray = [
{
name: "Hector Valdes",
photo: "",
scores: [
"5", "1",
"4", "4",
"5", "1",
"2", "5",
"4", "1"
]
}, {
name: "Tyler Williams",
photo: "",
scores: [
"5", "1",
"4", "4",
"5", "2",
"2", "5",
"4", "1"
]
}
]
let total = 0;
for (i = 0; i < peopleArray.length; i++){
for(j=0; j < peopleArray[i].scores.length; j++){
total += Number(peopleArray[i].scores[j]);
console.log(total);
};
};
You can use map to loop thru each peopleArray element. Use reduce to sum the scores.
One option in converting a string to number by adding + before the string.
var peopleArray = [{"name":"Hector Valdes","photo":"","scores":["5","1","4","4","5","1","2","5","4","1"]},{"name":"Tyler Williams","photo":"","scores":["5","1","4","4","5","2","2","5","4","1"]}];
var total = peopleArray.map(o => o.scores.reduce((c, v) => +c + +v));
console.log(total);
var peopleArray = [
{
name: "Hector Valdes",
photo: "",
scores: [
"5", "1",
"4", "4",
"5", "1",
"2", "5",
"4", "1"
]
}, {
name: "Tyler Williams",
photo: "",
scores: [
"5", "1",
"4", "4",
"5", "2",
"2", "5",
"4", "1"
]
}
]
let total = peopleArray.map(i => {
return i.scores.reduce((a, b) => parseInt(a) + parseInt(b), 0)
})
console.log(total)
Try this,
you need to move total in the first loop then just need to add a new total object in array
var peopleArray = [
{
name: "Hector Valdes",
photo: "",
scores: [
"5", "1",
"4", "4",
"5", "1",
"2", "5",
"4", "1"
]
}, {
name: "Tyler Williams",
photo: "",
scores: [
"5", "1",
"4", "4",
"5", "2",
"2", "5",
"4", "1"
]
}
];
console.log(peopleArray);
for (i = 0; i < peopleArray.length; i++){
let total = 0;
for(j=0; j < peopleArray[i].scores.length; j++){
total += Number(peopleArray[i].scores[j]);
};
peopleArray[i]['total'] = total;
console.log(total);
};
console.log(peopleArray);
Try this
const result = peopleArray.map((value) => {
return {
name: value.name,
score: value.scores.reduce((total, score) => total + Number(score), 0)
}
})
console.log(result);
Try this, Hope it helps..
let scoresArr = peopleArray.map(data => {
return data.scores.reduce((a,b) => parseFloat(a) + parseFloat(b), 0)
})
console.log(scoresArr)
I am getting a little confused. I have the following array of objects:
dataTest.rows = [{
"rowHeader": "",
"rowDesc": ["INFO1"],
"rowVal": [
["1", "2", "3", "4", "5", "6", "7", "8", "9"]
]
}, {
"rowHeader": "",
"rowDesc": ["INFO2"],
"rowVal": [
["1", "2", "3", "4", "5", "6", "7", "8", "9"]
]
}];
How would I fill up the array within the array rowval with a loop if rowVal, say in object1, was empty?
I tried
for (i=0; i<9; i++){
(dataTest.rows[0]).rowVal[0][i]=i;
}
Should I completely remove the label 'rowVal', and then add it to the object to rebuild it?
var dataTest={};
dataTest.rows = [{
"rowHeader": "",
"rowDesc": ["INFO1"],
"rowVal": [[]]
}, {
"rowHeader": "",
"rowDesc": ["INFO2"],
"rowVal": [[]]
}];
for (var i=0; i<dataTest.rows.length; i++){
// iteriates through the dataTest.rows array of objects
console.log("Entered row %i",i);
for (var j=1; j<=9; j++){
// converts number to string
dataTest.rows[i].rowVal[0][j]=j.toString();
console.log(dataTest.rows[i].rowVal[0][j]);
}
}
UPDATE: Fixed "dataTest.rows[i].rowVal[0] is undefined."
You can use the .forEach() Array method to loop through every item in array X and push it into array Y.
let dataTest= [{
"rowHeader": "",
"rowDesc": ["INFO1"],
"rowVal": []
},
{
"rowHeader": "",
"rowDesc": ["INFO2"],
"rowVal": [
["1", "2", "3", "4", "5", "6", "7", "8", "9"]
]
}];
dataTest[1].rowVal.forEach(number => {
dataTest[0].rowVal.push(number)
})
In this example, the forEach function runs through every item in the dataTest[1].rowVal and pushes everything inside into the array in dataTest[0].rowVal, which should achieve what you're asking.
Script:
vL1 = ["AB", "AB", "AB", "AB", "AB", "CS", "CS", "CS", "ND", "ND"];
vL2 = ["1", "1", "1", "2", "3", "1", "1", "2", "1", "1"];
for(var i = 0; i < vL1.length; i++){
thing = vL1[i] + " " + vL2[i];
console.log(thing);
}
When I check the developer console, I see the following:
(3) AB 1
AB 2
AB 3
(2) CS 1
CS 2
(2) ND 1
How can I modify the script so I can get the number of times AB with 1 appeared or CS with 1 appeared in my code to be used in other functions?
I just want to know the count for each vL2 that is represented in vL1. It is important to associate vL1 because that will let me identify the vL2, since it is not unique.
You might also do as follows;
var vL1 = ["AB", "AB", "AB", "AB", "AB", "CS", "CS", "CS", "ND", "ND"],
vL2 = ["1", "1", "1", "2", "3", "1", "1", "2", "1", "1"],
result = vL1.reduce((p,c,i) => p[c] ? (p[c][vL2[i]] = p[c][vL2[i]] ? ++p[c][vL2[i]]
: 1, p)
: (p[c] = {[vL2[i]]: 1}, p), {});
console.log(result);
You can store the counts in an object. Also, utilizing Array.prototype.reduce can make it simpler to work with the indexes (e.g. you don't have to handle incrementing the index manually, etc.):
vL1 = ["AB", "AB", "AB", "AB", "AB", "CS", "CS", "CS", "ND", "ND"];
vL2 = ["1", "1", "1", "2", "3", "1", "1", "2", "1", "1"];
var counts = vL1.reduce(function(counts,vL1Element,index) {
//initialize this index if it isn't set
if(counts[vL1Element] == undefined) {
counts[vL1Element] = {};
}
//set this count to 0 if it hasn't been set yet
if (counts[vL1Element][vL2[index]] == undefined) {
counts[vL1Element][vL2[index]] = 0;
}
counts[vL1Element][vL2[index]]++;
return counts;
},{});
console.log(counts);
var obj={};
function log(a){
if(obj[a]){
obj[a]++;
}else{
obj[a]=0;
}
}
And then do:
log(thing);
Inside of your for loop, and than:
console.log(obj);
Obj now contains:
AB1:3;
....