Can anyone explain the following Javascript code behaviour? - javascript

Here is my Javascript code:
var subRow = [];
var rowarr = [];
subRow.push({ v: "Jay" });
subRow.push({ v: "Ram" });
rowarr.push({ c: subRow });
subRow.length = 0;
subRow.push({ v: "Jay1" });
subRow.push({ v: "Ram1" });
rowarr.push({ c: subRow });
console.log(JSON.stringify(rowarr));
The output is:
[{
"c": [{
"v": "Jay1"
}, {
"v": "Ram1"
}]
}, {
"c": [{
"v": "Jay1"
}, {
"v": "Ram1"
}]
}]
The expected output is:
[{
"c": [{
"v": "Jay"
}, {
"v": "Ram"
}]
}, {
"c": [{
"v": "Jay1"
}, {
"v": "Ram1"
}]
}]
Can anyone explain why it so?

Arrays are handled by reference.
subRow.length = 0; erases the contents of the array.
rowarr then contains two pointers to the same array (which only has the content in it that you put there after emptying it)
Change subRow.length = 0; to subRow = [] to work on a new array instead of modifying the existing one.

subRow points to an object. When you push it onto rowArr you create a reference to that object. You push it twice, that's two references to one object. When you edit subRow both references to the object see the changes, so you've trampled all over the old contents of the object - they are not stored anywhere else, so they are completely lost. You need to create a brand new object instead of editing the old object.

Related

Changing certain object properties without overwriting the entire object

I am trying to overwrite an object given specific changes to that object. The problem is that there are other nested objects that get overwritten as well. How would I prevent this?
const deviceChanges = {
"a": 5,
"card": {
"back": [
{
"key": "iphoneText",
"label": "IPHONE",
"value": "UPDATED VALUE FOR IPHONE"
},
]
}
};
let newBody = {
"a": 3,
"card": {
"back": [
{
"key": "androidText",
"label": "ANDROID",
"value": "androidOS"
},
{
"key": "samsungText",
"label": "SAMSUNG",
"value": "samsungOS"
},
{
"key": "iphoneText",
"label": "IPHONE",
"value": "iphone"
},
{
"key": "macbookText",
"label": "MACBOOK",
"value": "macbookOS"
}
]
},
"c": 8
};
const expected = {
"object": {
"a": 5,
"card": {
"back": [
{
"key": "androidText",
"label": "ANDROID",
"value": "androidOS"
},
{
"key": "samsungText",
"label": "SAMSUNG",
"value": "samsungOS"
},
{
"key": "iphoneText",
"label": "IPHONE",
"value": "UPDATED VALUE FOR IPHONE"
},
{
"key": "macbookText",
"label": "MACBOOK",
"value": "macbookOS"
}
]
},
"c": 8
}
};
Here is a Unit Test example of what I am trying to do. I want to take the changes object, and basically replace b.x in newBody, but I also want to preserve the other fields like b.y, a, and C. I want to make it as dynamic as possible, so if there is another object for newBody.b.x or another value for A, I want the code to be able to notice that and adequately change that. Does anyone have an idea on what to do here?
for (let [key, value] of Object.entries(changes)) {
for(let [key1, value1] of Object.entries(value)) {
newBody[key][key1] = value1;
}
}
This is what I have so far in terms of Code. But it only takes into account the fact that it only needs to traverse through two objects to replace. If I had something like:
const changes = {
"b": {
"x": "new",
"y": {
"n": "iphone"
}
}
};
The code would not work. How do I make it as dynamic as possible to realize how many objects it needs to replace?
Here's a function that assigns values for matching keys from a source object into a target. It does so recursively.
Lodash _merge() does something like this, probably handling many more edge cases than I've anticipated here (which is approximately none).
const changes = {
"b": {
"x": "changed",
"Z": "new key/value pair"
}
};
let newBody = {
"a": 3,
"b": {
"x": "old",
"y": "fields"
},
"c": 8
};
// overwrite values in target with matching keys in source
// side-effects target, also returns it
function merge(target, source) {
for (const [key, value] of Object.entries(source)) {
if (key in target) {
if (typeof value === 'object' && typeof target[key] === 'object') {
merge(target[key], value);
} else {
target[key] = value;
}
} else {
// the key in source isn't in the target. add it
target[key] = value;
}
}
return target;
}
const r = merge(newBody, changes)
console.log(r)
If you don't know how deep an object/array is nested you often need to make use of recursive functions (functions that call themselves)
The function I wrote below is that.
I'll try to explain how it works:
For simplicities sake we're gonna assume there is only 1 property per "layer" (the function can actually handle multiple - since we loop, but to explain the recursive part we're just gonna simplify the whole thing)
So you start by checking if the property of the changes object is itself an object or not. If it not an object, this means we are at the deepest level of this particular nesting, so we can replace the property of our actual object with the property of the changes object. If the property is however an object we need to go deeper. So what we do in this case is call the function again but pass only the property object (in our case we would pass {x: "new"} and {x: "old", y: "fields"} - we do this until we don't find an object as our property in which case we'll replace.
This way we can go however deep we need and replace only properties that are primitives.
const changes = {
"b": {
"x": "new",
}
};
let newBody = {
"a": 3,
"b": {
"x": "old",
"y": "fields"
},
"c": 8
};
function doTheThing(theObject, changes) {
const keys = Object.keys(changes)
keys.forEach(key => {
if (typeof changes[key] === "object" && changes[key] !== null) {
//this will also be true if changes[key] is an array
doTheThing(theObject[key], changes[key])
} else {
theObject[key] = changes[key]
}
})
return theObject
}
console.log(doTheThing(newBody, changes))
So we solved that problem with recursion (there are tons of resources about that if you need more information)

getConnectedNodes direction parameter

I have a small issue with the parameter direction of the function getConnectedNodes() based on the Vis.js documentation (search for "getConnectedNodes" in the link)
Any idea to get the direction of the edges using the parameter (i don't know how to)?
JSON Example
[
{ "x": 0, "y": 0, "id": "0", "connections": [ 2 ] // i think here should be a from?},
{ "x": 200, "y": 0, "id": "1", "connections": [ 3, 2 ] },
{ "x": 500, "y": 500, "id": "2", "connections": [ 0, 1 ] },
{ "x": 300, "y": -200, "id": "3", "connections": [ 1 ] }
]
Here part of the code
google.script.run.withSuccessHandler(([nodes, edges]) => new vis.Network(container, {nodes: nodes, edges: edges}, options)).sample();
let network;
function init() {
container = document.getElementById('mynetwork');
exportArea = document.getElementById('input_output');
network = google.script.run.withSuccessHandler(([nodes, edges]) => {network = new vis.Network(container, {nodes: nodes, edges: edges}, options);}).sample();
};
function addConnections(elem, index) {
elem.connections = network.getConnectedNodes(index); < I THINK THE PROBLEM IS HERE
}
function exportNetwork() {
var nodes = objectToArray(network.getPositions());
nodes.forEach(addConnections);
var exportValue = JSON.stringify(nodes, undefined, 2);
exportArea.innerHTML = exportValue;
}
function objectToArray(obj) {
return Object.keys(obj).map(function(key) {
obj[key].id = key;
return obj[key];
});
}
Before hand, thanks a lot!
index is the index of the array like 0, 1, 2,,,. The start index is 0. On the other hand, elem is the object like {x: ###, y: ###, id: ###}. From these situation, I thought that index of getConnectedNodes(index) might be elem.id. So how about the following modification?
From:
elem.connections = network.getConnectedNodes(index);
To:
elem.connections = network.getConnectedNodes(elem.id, "from");
From the document, if you want to retrieve "parent", you can retrieve it by adding from to the argument.
For a node id, returns an array with the id's of the connected nodes.
If optional parameter direction is set to string 'from', only parent nodes are returned.
If direction is set to 'to', only child nodes are returned.
Any other value or undefined returns both parent and child nodes.
When you want to retrieve "child", please add to to the argument instead of from.

How order a JSON alphabetically in javascript, like this? [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 can ordering a-z using the .sort() method in javascript, but I would get a JSON like this: (With a "A-Z" index containing the result)
data: {
"A": [
{
"name": "Anh Tuan Nguyen",
"age": 28
},
{
"name": "An Nguyen",
"age": 20
},
],
"Z": [
{
"name": "Zue Dang",
"age": 22
},
{
"name": "Zoom Jane",
"age": 30
},
]
}
var names = [{name:"a1"},{name:"a2"},{name:"b1"},{name:"b2"}.....];
var data = {};
for (var i = 0; i < names.length; i++) {
var n = names[i].name.subStr(0,1);
if (data[n] == null)
data[n] = [];
data[n].push(names[i]);
}
There is no way to sort a JSON data structure, however, we can do it by using the following process:
Get your data keys with Object.keys(myResults.data)
Sort you keys
Create a reduce function to transform your ordered keys into an ordered object
The snippet is here, I hope it helps:
var myResults = {
data: {
C: [
{
"name": "Kevin Doe",
"age": 22
}
],
A: [
{
"name": "Alex Doe",
"age": 31,
}
],
B: [
{
"name": "Boris Doe",
"age": 22
},
{
"name": "Birdo Doe",
"age": 30
},
]
}
};
var originalData = myResults.data;
// 1. get the keys
var dataKeys = Object.keys(originalData);
// 2. sort the keys
var sortedKeys = dataKeys.sort();
// 3. create the object again
var orderedData = sortedKeys.reduce(function(result, key) {
return Object.assign(
{},
result,
{ [key]: myResults.data[key] }
);
}, {});
document.getElementById('original').innerHTML = JSON.stringify(originalData);
document.getElementById('sorted').innerHTML = JSON.stringify(orderedData);
h3 {
margin: 0;
}
code {
display: block;
margin-bottom: 15px;
padding: 10px;
background-color: #f9f9f9;
}
<h3>Original Data</h3>
<code id="original"></code>
<h3>Ordered Data</h3>
<code id="sorted"></code>
JavaScript objects are not ordered. If you want to iterate over an object's properties, you can sort the keys and then retrieve your values:
const result = {
data: {
Z: [],
F: [],
A: [],
D: []
}
};
Object
.keys(result.data)
.sort()
.map(key => console.log(key, result.data[key]));
UPDATE:
Exist a JavaScript library that make It possible: Lodash Utilities (https://lodash.com/docs/4.17.4). Contain methods for .sort() JSON (no Arrays) and a method to obtain the JSON for I asked in this question. I only did this:
//First, order my JSON alphabetically with _.sortBy method (I can even order by a nested property in my JSON)
var order = _.sortBy(require('./names'), function (el) { return el.name });
//Second, I group my order result by the first letter of my property 'name' in my JSON
var list = _.groupBy(order, (b) => b.name[0].toUpperCase());
This is my input:
[
{"name":"Mark"},
{"name":"Jul"},
{"name":"Alicia"},
]
This is my output:
[
"A": [
{
"name": "Alicia"
}
],
"J": [
{
"name": "Jul"
},
],
"M": [
{
"name": "Mark"
},
]
I hope this help to somebody!

how to filter the data by obj's attribute in ng-repeat angularjs

im a beginning student on angularjs
suppose we have a josn obj like
[ {
"Title": "a",
"Date": "2015-05-31",
"a": "11",
"b": 22,
},
{
"Title": "b",
"Date": "2015-05-11",
"a": "33",
"b": 44,
},
{
"Title": "c",
"Date": "2015-04-11",
"a": "55",
"b": 66,
},
{
"Title": "d",
"Date": "2015-03-03",
"a": "11",
"b": 22,
}
]
ngRepeat
<li ng-repeat="obj in objs">{{obj.Date | date:"MM/yyyy"}}</li>
we have two data in MAY (05-31 ,05-11)in the json obj,and
i want to keep one data in a month, just keep the second one.
how to write the fliter i'm confused
You need to create a filter and use it inside ng-repeat.
The syntax to create filters is like this:
angular
.module('myModule')
.filter('myFilter', function myFilter(service1, service2) { //to ask for deps
return function(input) { //here you do the real filtering
return doSomethingWithInput(input);
}
})
To apply the logic filtering you ask for, you can create a filter like this:
angular
.module('myModule')
.filter('oncePerMonth', oncePerMonth);
function oncePerMonth() {
return function (data) {
var filtered = [], months = [];
data.forEach(function(item){
var month = item.Date.substr(0,7);
if (~~months.indexOf(month)){
months.push(month);
filtered.push(item);
}
});
return filtered;
}
}
With this filter, the html template would be something like this:
<li ng-repeat="obj in vm.objs | oncePerMonth">
{{obj.Date | date:"MM/yyyy"}} {{obj.Title}}
</li>
You can find a working Plnkr here: http://plnkr.co/edit/sg0wWPuUE0cJwoZj2KuS?p=preview

How to get json child value of array key pair in javascript?

I have the following JSON variable:
var jsonObj= { "ClassA": { "A": "111", "B": "222", "C": "333", "D": "444", "E": "555", "F": "666", "G": "777" }, "ClassB": { "A":"22","B":"33","C":"44","D":"55","E":"66","F":"77","G":"AAA" }};
How do I get the class A value for key A ?
I am writing a function to allow for me to get these, something like:
function getDisplayValue(turnOverBracketCategory, classTypeAorB) {
if(classTypeAorB == "A") {
alert("1");
return jsonObj.ClassA[turnOverBracketCategory];
} else {
alert("3");
return jsonObj["ClassB"].key[turnOverBracketCategory];
}
}
Where turnOverBracketCategory is the key ("A", "B", etc.) and the classTypeAorB defines if using "ClassA" or "ClassB".
You can access ClassA + A doing this:
jsonObj.ClassA.A
will return 111
You can get the keys like this
Object.keys( jsonObj.ClassA );
will return "A","B"....
Thanks! Not exactly what I wanted, but good to know that I can also access the key. Althouh, what I am looking for is the value...
I already had the answer, the issue was that the variable was not being correctly populated.
Cheers.
To get the value I would do the following
jsonObj.ClassA[turnOverBracketCategory]
or
jsonObj.ClassA["A"]

Categories