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>
My Code Scenario is:
var Employees= [{name:"Ram",htno:1245},{name:"mohan",htno:1246},
{name:"madhu",htno:1247},{name:"ranga",htno:1248}]
var seletedEmployees= [{name:"mohan"},{name:"ranga"}];
var employeesdataAfterremoveSelected = [?];
You can store selected employees names in an array and then filter Employees array and check if employee's name is in this array:
var employees= [{name:"Ram",htno:1245},{name:"mohan",htno:1246},{name:"madhu",htno:1247},{name:"ranga",htno:1248}]
var selectedEmployees= ["mohan","ranga"];
var result = employees.filter(emp => selectedEmployees.includes(emp.name));
console.log(result);
To programatically get array of strings instead array of objects, you can use map:
var seletedEmployees= [{name:"mohan"},{name:"ranga"}].map(emp => emp.name);
From the code you have given above i think this might work
$.each(student, function(key, value){
if(matchedvalues.indexOf(value.name) < 0)
{
employeesdataAfterremoveSelected.push(value.name);
}
})
Here is a one liner, decomposed to explain :
// Start by filtering the first array on a condition.
employeesdataAfterremoveSelected = Employees.filter(
// Map the array of selected employees to only return the name
e => seletedEmployees.map(_e => _e.name)
// use the includes function to check if the name is in the array
.includes(e.name)
);
In one line :
employeesdataAfterremoveSelected = Employees.filter(e => seletedEmployees.map(_e => _e.name).includes(e.name));
You can use the filter method, something like below (not tested)
var Employees = [{name:"Ram",htno:1245}, {name:"mohan",htno:1246}]
var SelectedEmployess = [{name:"Ram",htno:1245}]
// filter the items from the invalid list, out of the complete list
var employeesdataAfterremoveSelected = Employees.filter((item.name) => {
return !SelectedEmployess.has(item.name);
})
// get a Set of the distinct, valid items
var validItems = new Set(employeesdataAfterremoveSelected);
You can try this:
var Employees = [{name:"Ram",htno:1245},{name:"mohan",htno:1246},
{name:"madhu",htno:1247},{name:"ranga",htno:1248}]
var seletedEmployees = [{name:"mohan"},{name:"ranga"}];
var employeesdataAfterremoveSelected = Employees.filter(name => {
return (name.name !== seletedEmployees[0].name && name.name !== seletedEmployees[1].name)
})
console.log(employeesdataAfterremoveSelected)
var Employees= [{name:"Ram",htno:1245},{name:"mohan",htno:1246},
{name:"madhu",htno:1247},{name:"ranga",htno:1248}]
var seletedEmployees= [{name:"mohan"},{name:"ranga"}];
var employeesdataAfterremoveSelected = Employees.filter(function(val,index) { console.log(val.name)
return !(seletedEmployees.map(function(e) { return e.name; }).indexOf(val.name));
});
So I have a dynamic variable that could be any integer from 5 to < 99 decided by the user.
var topLevelMenusTotalNum
Each top level menu has 5 fixed properties that I want to store which are mostly integers and some long numbers. And then recall for use in my bit of code.
Whats is the bext way for me to store these values in a storage system that can be dynamic in size?
I'm guessing I should store each top level menu as an object, with the 5 properties, i.e.
menu1.property1 = 500
menu1.property2 = 23
...
menu1.property5 = 24.3445345644
But how can I dynamically create the menu1, menu2, menu3 etc objects depending on how many the user has created?
Should I create the objects in an array? Or something else?
Does the order matter? If so, use an array.
Does the name matter? If so, use an object with named properties.
Do neither matter? Then it doesn't really make a difference. Arrays are slightly easier to loop over.
if you have an object, you can dynamically add items to it like so:
var menus = {
"menu1": {
//properties
},
"menu2": {
//properties
} //etc...
}
then you could add to it like so:
menus['menu' + newMenuNR] = {'property1': 21, 'property2': 10} //<-- properties in there
this is fully dynamic and won't result in problems later on, to loop through the object, you could use a 2-dimentional loop.
for(menu in menus) {
for(item in menu) {
alert(item.['property1']); //displays property of item in menu in menus (replace property with your own property names)
}
}
I will suggest you to use an object for top layer and each object will contain array as class member.
Object will help you to create dynamically based on user and each object of user will contain an array which has five properties.
I would suggest doing it with objects as long as the parameters can be named.
If you need a lot of objects, just keep an array of objects to search/sort/filter.
//Class (Hide this in some library script)
var Menu = (function () {
function Menu(parameters) {
if (parameters === void 0) { parameters = {}; }
this.node = document.createElement("ul");
this.items = [];
this.width = 100;
this.height = 100;
this.name = "";
this.title = "";
this.children = [];
//Apply parameters
for (var key in parameters) {
if (parameters.hasOwnProperty(key) && this.hasOwnProperty(key)) {
this[key] = parameters[key];
}
}
//Apply node parameter
this.node.title = this.title;
//Add to static
Menu._menus.push(this);
}
Menu.prototype.render = function () {
//Reset contents
this.node.innerHTML = "";
//Append sub-menues
for (var childIndex = 0; childIndex < this.children.length; childIndex++) {
var child = this.children[childIndex];
var li = document.createElement("li");
li.appendChild(child.render());
this.node.appendChild(li);
}
//Append generic items
for (var itemIndex = 0; itemIndex < this.items.length; itemIndex++) {
var item = this.items[itemIndex];
var li = document.createElement("li");
li.innerHTML = item;
this.node.appendChild(li);
}
//Return node
return this.node;
};
Menu._menus = [];
return Menu;
}());
//Implementation
//create menu
var topMenu = new Menu({ items: ["Just testing"], title: "Super!" });
//Add anonymous submenues
topMenu.children
.push(new Menu({ items: ["item 1"], title: "sub", name: "sub1" }), new Menu({ items: ["item 3", "item 2"], title: "sub", name: "sub2" }));
//Add to "bigList"
document.getElementById("bigList").appendChild(topMenu.render());
//Updates incoming
setTimeout(function () {
//Find menu with the most items + children (Which is easy with named parameters)
var m = Menu._menus
.filter(function (a) {
return a.title == "sub";
}).sort(function (a, b) {
return (b.items.length + b.children.length) - (a.items.length + a.children.length);
})[0];
//Add new item
m.items.push("Sweet right?");
//Update node
m.render();
}, 2000);
setTimeout(function () {
//Find last menu
var m = Menu._menus
.reverse()[0];
//Remove last item
m.items.splice(m.items.length - 1, 1);
//Update node
m.render();
}, 4000);
<div id="bigList">
Named parameters are lovely :-)
</div>
I have an array of Exclusions like below:
Exclusions: [ID:"233242", Loc:"West", ID:"322234" , Loc:"South"]
I also Object nested with an array of objects that could look something like
Schools : [ O: [ ID:"233242" ] , 1:[ ID:"233242"] , 2: [ID :"954944"] ]
I need to delete from the schools object any matching array indices with the same ID but only for the first match. That means element 0 should be removed, but element 1 should still be there. What's the best way to fix my loop:
$.each(Exclusions, function (index, value) {
var loc = value.Loc;
var ID = value.ID;
Object.keys(Schools.District.Pack[loc]).forEach(function (key) {
//i need to scan through the entire object
if (Schools.District.Pack[loc].ID === ID) {
//remove the first match now stop looking
Schools.District.Pack[loc].splice(key, 1);
//break ; incorrect
}
});
});
I'd say having another lookup array for removed IDs, and you'll need something like this
var Exclusions = [{ID:"233242", Loc:"West"}, {ID:"322234" , Loc:"South"}];
var Schools = [{ ID:"233242" } ,{ ID:"233242"} , {ID :"954944"} ];
var removedKeys = [];
$.each(Exclusions, function (index, value) {
var loc = value.Loc;
var ID = value.ID;
Object.keys(Schools).forEach(function (key) {
//i need to scan through the entire object
if ((Schools[key].ID === ID) && (removedKeys.indexOf(ID) == -1)) {
removedKeys.push(ID);
//remove the first match now stop looking
delete Schools[key];
}
});
});
console.log(removedKeys);
console.log(Schools);
Hope this would help
fiddle
I want to store elements as the keys in my array and object as values,for example -
var arr = [];
arr[ document.getElementById('something') ] = { data: 'something' , fn : function(){ } };
But the problem is: If I will add another element with the key of : document.getElementById('otherthing').
And later will try to get the value of : arr[ document.getElementById('something') ].data , I will get the value of arr[ document.getElementById('otherthing') ].For Example :
var arr = [];
arr[ document.getElementById('something') ] = { data: 'something' , fn : function(){ } };
arr[ document.getElementById('otherthing') ] = { data: 'otherthing' , fn : function(){ alert('k'); } };
alert( arr[ document.getElementById('otherthing') ].data ); // alerts "otherthing"
alert( arr[ document.getElementById('something') ].data ); // alerts "otherthing" but suppose to alert "something"
How I can fix this problem,I can`t save by id,because I want to support other nodes with no-id
Thanks,Yosy.
EDIT:My answer to this,If you have better answer please write it :) (Inspired by casablanca`s answer)
array for id: key-integer the node id, value the node it self
and the array with data and fn with key of my id,It will look like this :
var idArray = [],nodeArray = [];
idArray[0] = document.getElementById('hello_ducks');
nodeArray[0] = { data: 'hello ducks!!' , fn : function(){ alert('k'); } };
idArray[1] = document.getElementById('hello');
nodeArray[1] = { data: 'hello' , fn : function(){ } };
var testNode = document.getElementById('hello_ducks'), foundId = -1 /*found id*/;
// Do we have testNode in our array?
for(var i = 0 ; i < idArray.length; i++ ){
if( idArray[i] === testNode ){
foundId = i;
}
}
// Do we found our element?
if(foundId >= 0) {
alert( nodeArray[foundId].data ); // "hello ducks!!"
}
I can`t save by id,because I want to support other nodes with no-id
You should keep in mind that in order to get back a node from the array, you have to somehow identify it. If a node doesn't have any such unique identifier, then how do you retrieve it later from the array, or even store it in the first place?
In lower-level languages like C++, every object implicitly has a unique address, but there is no such thing you can use in JavaScript, so you need to manually provide some way of identifying an object, and the DOM ID is the most convenient way of doing this.
If some of your nodes don't initially have an ID, the best way to proceed is to simply assign your own unique ID.
Update: The solution you posted will work, but it's not very efficient because you need to search the entire array every time you need to find a node. Why not simply assign your own ID to those elements which don't have one? For example:
var nextID = 0;
function getID(elem) {
if (elem.hasAttribute('id') == false || elem.id == '')
elem.id = 'dummy-' + (++nodeID);
return elem.id;
}
This way you can always use the ID as a key:
var nodeArray = [];
var e = document.getElementById('hello');
nodeArray[getID(e)] = { ... };
var e = /* element obtained from somewhere, doesn't have an ID */
nodeArray[getID(e)] = { ... }; // still works
I would suggest that instead of using an array for this purpose, you take advantage of the DOM-elements being objects:
document.getElementById('something').info = {data: 'something', fn: function () { } };
but you may end up with some issues with memory leaks in certain older browsers (read: ie6). That is fixed if you use jQuery's data storage instead:
$("#something").data("info", {data: 'something', fn: function () { } });