I have a JSON query and I am using console.log to present it:
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
document.getElementById("original").innerHTML = json_data.rows;
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
And for the value "one" I have two numbers (209 and 274).
I am then using a function to groupby which works fine (output). My problem is that when I use the console.log for the initial json_data query, the first two rows are different. It seems that my function replaced the first two rows with the rows of the output (red). The function is given here:
function initialGroupBy(rows) {
const
rowMap = new Map(),
result = [],
dataTemp = [];
// Iterate over the rows.
rows.forEach(row => {
const
// Create a key, it is the first elements joined together.
key = row.slice(0,1).join();
// Check if the Map has the generated key...
if (rowMap.has(key)) {
// The map has the key, we need to add up the values
const
// Get the value for the current key.
storedRow = rowMap.get(key);
// Add the value of the current row to the row in the map.
storedRow[2] += row[2];
} else {
// The key doens't exist yet, add the row to the map.
rowMap.set(key, row);
}
});
// Iterate over all the entries in the map and push each value with the
// summed up value into the array.
rowMap.forEach(value => {
result.push(value);
});
for (i = 0; i < result.length; i++)
{
var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
dataTemp.push(object2);
}
return dataTemp;
}
A full snippet can be found here (Compare the first two rows of the yellow box from the two snippets):
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
function initialGroupBy(rows) {
const
rowMap = new Map(),
result = [],
dataTemp = [];
// Iterate over the rows.
rows.forEach(row => {
const
// Create a key, it is the first elements joined together.
key = row.slice(0,1).join();
// Check if the Map has the generated key...
if (rowMap.has(key)) {
// The map has the key, we need to add up the values
const
// Get the value for the current key.
storedRow = rowMap.get(key);
// Add the value of the current row to the row in the map.
storedRow[2] += row[2];
} else {
// The key doens't exist yet, add the row to the map.
rowMap.set(key, row);
}
});
// Iterate over all the entries in the map and push each value with the
// summed up value into the array.
rowMap.forEach(value => {
result.push(value);
});
for (i = 0; i < result.length; i++)
{
var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
dataTemp.push(object2);
}
return dataTemp;
}
const damn = initialGroupBy(json_data.rows);
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="background:yellow; "id="original"></div>
<br><br>
<div style="background:red;" id="output"></div>
I have tried changing the var to const in many cases. Am I missing a fundamental JavaScript case here?
As your value is an Array object, when you save it in your temporary Map, you're actually using a reference to your original data structure row.
So in the first forEach loop, where you sum the values you're actually changing the original array entry.
The solution is pretty simple, just clone the array:
rowMap.set(key, row.slice());
Another possibility is to use a different array to save the totals.
Here is your code with the fix.
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
function initialGroupBy(rows) {
const
rowMap = new Map(),
result = [],
dataTemp = [];
// Iterate over the rows.
rows.forEach(row => {
const
// Create a key, it is the first elements joined together.
key = row.slice(0,1).join();
// Check if the Map has the generated key...
if (rowMap.has(key)) {
// The map has the key, we need to add up the values
const
// Get the value for the current key.
storedRow = rowMap.get(key);
// Add the value of the current row to the row in the map.
storedRow[2] += row[2];
} else {
// The key doens't exist yet, add the row to the map.
rowMap.set(key, row.slice());
}
});
// Iterate over all the entries in the map and push each value with the
// summed up value into the array.
rowMap.forEach(value => {
result.push(value);
});
for (i = 0; i < result.length; i++)
{
var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
dataTemp.push(object2);
}
return dataTemp;
}
const damn = initialGroupBy(json_data.rows);
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
A) The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable -> Link
B) Your problem is that you are actually editing the original object in the initialGroupBy function. Maybe this answer will be helpful.
A different logic applies here and the result is handy:
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
function groupBy(accumulator, item) {
const [date,extra,value] = item;
const key = date.slice(0,7);
if(!accumulator[key]){
accumulator[key] = 0
}
accumulator[key] += value;
return accumulator;
}
var damn = json_data.rows.reduce(groupBy,{});
damn = Object.keys(damn).map(function(key){
return {date: key, Value: "Total", num: damn[key]};
})
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
this is my code:
start() {
let columns = ['A'...'Z'];
let fields = {
id: [
'Medlemsnummer',
],
name: [
'Namn',
],
};
let out = {};
let self = this;
columns.forEach(function(column) {
for(let row = 1; row < 101; row++) {
let cell = column + row;
let d_cell = self.worksheet[cell];
let val_cell = (d_cell ? d_cell.v : ' ');
let cell_string = val_cell.toString().toLowerCase();
let cellString_stripped = cell_string.replace(/(?:\r\n|\r|\n)/g, '');
for (var key in fields) {
// skip loop if the property is from prototype
if (!fields.hasOwnProperty(key)) continue;
var obj = fields[key];
for (var prop in obj) {
// skip loop if the property is from prototype
if(!obj.hasOwnProperty(prop)) continue;
obj.forEach(function(term) {
if(cellString_stripped.match(new RegExp(term.toLowerCase() + ".*"))){
//out.push(obj + ': ' + cell);
//out[obj] = {cell};
out[obj] = cell;
}
});
//out[obj]
}
}
}
});
console.log(out);
},
and my problem is that i want several matched cells in out[obj] = // array of matched cells.
how can i do this in javascript?
so my out should look like this:
out = [ medlemsnummer: ['A11','A23','A45'], name: ['B11','B23'] etc... ]
please comment if you need me to explain better.
Kind regards,
Joakim
Looking at your loops, I think you got a little lost in your own structures. out[obj] = cell definitely doesn't seem right; obj is an object, it cannot be used as a key in another object. Here's my take with some notes, hope I interpreted both your code and your question correctly. I'm starting from the loop after all your variables like cell, d_cell, etc. are initialized):
for (let key in fields) {
if (!fields.hasOwnProperty(key)) continue;
let terms = fields[key];
// fields[key] yields us an array, e.g.:
// fields['id'] = [ 'Medlemnummer' ]
// so we can iterate over it directly with for..of.
// Note also: variable names like "obj" make reading your code
// difficult; use meaningful names, e.g. "terms".
for (let term of terms) {
let regex = new RegExp(term.toLowerCase() + ".*");
// Note: RegEx.test() is more efficient than String.match()
// if all you need is a yes/no answer.
if (!regex.test(cellString_stripped)) continue;
// Here's the part you actually needed help with:
if (!out[term]) {
out[term] = [];
}
out[term].push(cell);
}
}
Addendum: In the code I'm sticking with your solution to use RegExp to test the strings. However, if all you need to check is whether the string starts with the given substring, then it's much shorter and more efficient to use String.startsWith():
for (let term of terms) {
if (!cellString_stripped.startsWith(term.toLowerCase())) continue;
// Here's the part you actually needed help with:
if (!out[term]) {
out[term] = [];
}
out[term].push(cell);
}
I am working with an api that provides a response like this:
{
"statuses": {
"status": [
{
"#array": "true",
"#id": "1",
"#uri": "https://api.com/people/statuses/1",
"name": "Member",
...
]
}
}
I need to use javascript to strip the # out of the key names. Eg "#id" to "id". How can I do this?
You'll have to manually iterate and rename each key and remove the original attribute in a given status. Something like
response.statuses.status.forEach(function(status) {
var keys = Object.keys(status),
keyCount = keys.length;
for (var j = 0; j < keyCount; j++) {
var thisKey = keys[j];
status[thisKey.replace('#', '')] = status[thisKey];
delete status[thisKey];
}
});
Other than looping through the keys and building a new object like everyone else has stated, you could get really crazy and do something like this:
// convert to string, if not already a string response
var responseStr = JSON.stringify(myObj);
// REGEX AWAY THE STUFF
responseStr = reponseStr.replace(/#/g, "");
// convert to obj
myObj = JSON.parse(responseStr);
Although, if any of your data has # in it naturally, it would get regex'ed out. I know, It's crazy. Thought it might be worth mentioning.
You can't just rename the keys within javascript objects, but you could always extend the response object. Something like this would work:
for(var i = 0; i < statuses.status.length; i++){
var status = statuses.status[i];
status.array = status["#array"];
status.id = status["#id"];
status.uri = status["#uri];
// could also delete properties here... see below...
}
That would give you what you were looking for, although all the properties with '#' would still exist. You could take it a step further and delete those properties, though.
delete status["#array"];
delete status["#id"];
delete status["#uri"];
I hope that's what you're looking for. Let me know if that helps!
function stripSymbol(obj) {
var type = Object.prototype.toString.call(obj);
if (type === '[object Array]') {
for (var i = 0, len = obj.length; i < len; i++) {
stripSymbol(obj[i]);
}
} else if (type == '[object Object]') {
Object.keys(obj).forEach(function(key) {
if (key[0] === '#') {
var newKey = key.substr(1);
obj[newKey] = obj[key];
delete key;
key = newKey;
}
stripSymbol(obj[key]);
});
}
}
stripSymbol(data);
This will recursively crawl through and object and remove the '#' at the beginning of any keys.
well first of all array is probably a reserved word for any javascript parser but ignoring that try this...
statuses.status[0].array = statuses.status[0]["#array"];
delete statuses.status[0]["#array"];
the reason you need to do this is because javascript doesn't support renaming keys.
When i create associate array in javascript, i got a problem like that.
I want to get the value by using field name as key, but i just only got undefined.
What should i do to get value by key or which way is good approach for it.
Here is my code
function getFields(pVal){
var tmpObj = {};
str = pVal.split(",");
for(i=0;i<str.length;i++){
tmpVal = str[i].split(":");
tmpObj[tmpVal[0]] = tmpVal[1];
}
return tmpObj;
}
function JustTest(){
var fields = {};
fields = getFields("'Code':'PRJ001','Name':'Project 01'");
alert(fields['Code']);
}
Because the key is 'Code', not Code, note the single quote ', you need do alert(fields["'Code'"]);
PS: Please add ; at the end of statement, it is bad practice to omit them.
I have re-factor the code, just try this:
function getFields(pVal) {
var tmpObj = {};
var str = pVal.split(",");
for (var i = 0; i < str.length; i++) {
var tmpVal = str[i].split(":");
tmpObj[tmpVal[0]] = tmpVal[1];
}
return tmpObj;
}
function JustTest() {
var fields = { };
fields = getFields("'Code':'PRJ001','Name':'Project 01'");
alert(fields["'Code'"]);
}
if you have question please comment below about code, thanks