Why create arrays of properties in D3 Force-Directed Graph example? - javascript

I'm adapting Mike Bostock's D3 Force Directed Graph example for a project.
I don't understand why he creates arrays of single properties from the nodes/links then references those arrays later on.
For example:
// this creates an array of node ids like ["id1", "id2", ...]
const N = d3.map(nodes, nodeId).map(intern);
// ...
nodes = d3.map(nodes, (_, i) => ({id: N[i]}));
// ...
const forceLink = d3.forceLink(links).id(({index: i}) => N[i]);
Why not just do:
nodes = d3.map(nodes, (d) => ({id: d.id}));
// ...
const forceLink = d3.forceLink(links).id((d) => d.id);
It's a lot simpler, and the end result seems to be the same. But this is an official example from the creator of D3, so perhaps there's a reason he did it that I'm not understanding?
And actually he has an example where he doesn't create arrays here.
I tried logging the variable values to understand the code, but I still don't understand why he did it that way.

The example builds a configurable component ForceGraph that is a function which takes two arguments: 1) an object containing the properties nodes and links plus 2) an object containing properties for customizing the ForceGraph component:
function ForceGraph({ // <== first parameter, nodes and links
nodes, // an iterable of node objects (typically [{id}, …])
links // an iterable of link objects (typically [{source, target}, …])
}, { // <== second parameter, configuration
nodeId = d => d.id, // given d in nodes, returns a unique identifier (string)
/* more config options */
} = {}) {
// Compute values.
const N = d3.map(nodes, nodeId).map(intern);
const LS = d3.map(links, linkSource).map(intern);
const LT = d3.map(links, linkTarget).map(intern);
The function ForceGraph defines a reusable component which can take data for nodes and links in an arbitrary format. That's why the comment on the nodes line says
an iterable of node objects (typically [{id}, …])
The configuration object's (i.e. the second parameter) first property defines an accessor function used to extract a unique identifier for the nodes:
nodeId = d => d.id, // given d in nodes, returns a unique identifier (string)
That is the reason why there is an itermediate array N containing those identifiers being computed using said accessor function. The array N (as well as all the other helper objects and arrays) is used to normalize the external data structure to the internal representation:
// Replace the input nodes and links with mutable objects for the simulation.
nodes = d3.map(nodes, (_, i) => ({id: N[i]}));
links = d3.map(links, (_, i) => ({source: LS[i], target: LT[i]}));
The intermediate step provides an abstraction of the internal data from the external data. This ensures that the inner workings of ForceGraph can always act on the same pre-defined data structure, no matter what the external data structure might look like.
If, for example, your nodes' data looks like
data = {
nodes : [{
key: { main: "m", sub: "1" }
}, {
key: { main: "m", sub: "2" }
}, {
/* ...*/
}],
links: { /* ... */
};
You can call ForceGraph passing a custom accessor function that returns unique copound keys for your nodes:
graph = ForceGraph(data, {
nodeId: d => d.key.main + d.key.sub
})
Thanks to the intermediate transformation of the input data this is all it takes to make the graph work without the need to change any of the inner workings of the component.

It's a matter of best practices. N is not an array of single properties (you probably meant "array of objects with a single property"), it's an array of primitives. Also, Bostock is using the JavaScript convention of using all caps for constants, like N (for nodes), LS (for link sources), LT (for link targets) etc.
The clue comes just after that, in his comment:
// Replace the input nodes and links with mutable objects for the simulation.
Thus, nodes and links are mutable arrays, which the simulation manipulates (the simulation itself adds several properties to each object), while he keeps a reference to the arrays with the original, immutable values. Finally, notice that objects are mutable, while strings (a primitive) are not.

Related

arrays disappearing in method scope

I am trying to create a class to my javascript game to add multiplayer but within the class i am having problems with the values of arrays changing as you can see in the sendNetEntities() function
class NET_IO{
//probably put address here
//I want to check for localhost to denote MASTER client
constructor(host, netlayer){
this.socket = io();
this.netLayer = netlayer
console.log(this.socket)
this.netEntities = this.netLayer.entities
//setInterval(() => {this.update()}, 200)
}
getNetEntities(){
this.socket.emit('getNetEntities', (net_entities) => {
console.log(net_entities)
this.netEntities = net_entities
})
}
sendNetEntities(layer){
var netEnt = this.netEntities
console.log(netEnt) //this returns [background: Entity, NIkTag: Entity, player: Entity]` the value i want
var ent = JSON.stringify(netEnt);
console.log(ent) //this returns []
this.socket.emit('sendNetEntities', ent)
}
update(layer, callback){
//check host if localhost dont retreive new data only send
this.sendNetEntities(layer)
callback(this.netEntities)
}
}
I think im having problems with variables somehow being references of something instead of instances. But im not entirely sure all of the rules behind that for javascript. can anyone help me shed some light on this problem. I'm willing to edit my question as needed
EDIT
further debugging leads me to believe that it must be some sort of problem with socket.io. if i run this this.socket.emit('sendNetEntities', {netEnt}) my return on the server is {netEnt:[]} I havent had problems like this in socket.io in the past. Am i doing something wrong. is socket.io the problem
Based on this:
//this returns [background: Entity, NIkTag: Entity, player: Entity]` the value i want
console.log(netEnt)
var ent = JSON.stringify(netEnt);
console.log(ent) //this returns []
I think you are treating an Array as an Object. In JavaScript, this is technically possible because almost everything is an Object, including arrays. However, this may lead to unexpected behavior:
// Create an array and an object
a = [] // an array
o = {} // an object
// Set some properties on both
a.p = 42
o.p = 42
// Show differences between arrays and objects:
console.log(a.constructor) // ƒ Array()
console.log(a) // [p: 42]
console.log(JSON.stringify(a)) // []
console.log(o.constructor) // ƒ Object()
console.log(o) // {p: 42}
console.log(JSON.stringify(o)) // {"p":42}
As you can see, JSON.stringify() ignores properties set on arrays.
So the solution is to use netEnt either as an array or as an object, without mixing the types:
// As an array, don't use property names. Use the integer array indices:
netEnt = [entity1, entity2, entity3]
background = netEnt[0]
nikTag = netEnt[1]
player = netEnt[2]
// As an object, property names can be used:
netEnt = {background: entity1, NIkTag: entity2, player: entity3}
background = netEnt.background
nikTag = netEnt.NIkTag
player = netEnt.player
update:
The fundamental problem is your classes use arrays, but access them as objects. The best solution is to change your classes so they either:
use arrays and access the arrays as arrays.
use objects and access the objects as objects.
Without seeing your class definitions, I cannot show you how to do this. However, it is as simple as changing the initial value of the class instances from [] to {}.
The following is a quick fix that serializes your array "objects" into true JS objects so JSON.stringify() will work as expected. However, in the future I highly recommend learning the difference between JS arrays and objects. This quick fix imposes a totally unnecessary performance penalty because JS arrays are being misused as objects:
sendNetEntities(layer){
var netEnt = this.netEntities
// Convert netEnt array "object" into true JS object
var trueObject = {}
for (prop in netEnt) {
trueObject[prop] = netEnt[prop]
}
var ent = JSON.stringify(trueObject);
this.socket.emit('sendNetEntities', ent)
}
Note in getNetEntities(), you will probably have to do the reverse: convert from true JS objects back to array "objects." I was unsure of the input format of net_entities, so I left this as an exercise.

Javascript performance and comparing objects vs properties of the object

In javascript is it faster to compare via a property or the entire object that has tons of properties? Below is what I currently have but my object has a ton of properties, and the objects list is quite large. Is it better to create a list from a single property off say id and compare the id of the object? objectids.indexOf(object1.id). Would I see a performance increase?
Comparing Against The Object
objects.indexOf(object1);
function Object() {
this.id = 1;
this.name = "test";
}
Note that both versions aren't equivalent:
({ id: 1}) === ({ id: 1 }) // false
({ id: 1}).id === ({ id: 1 }).id // true
If both cases work the same indexOf has to traverse half the array on average to get the index, it doesn't really matter if that array is an array of ids or objects. To get O(1) lookup time use a Set instead.

Javascript data structure: Set of page widths with associated values

I am trying to get a set of classes associated with different window widths. These class-to-width pairs are set by the user. However, I can't find the correct data structure to store it. I believe a tuple would be the optimal type but apparently they don't exist in javascript. If they did my data would looks like this:
var pageBreaks = [(900, "foo"), (600, "bar")];
Where at 900px I could apply the "foo" class. 600px I could apply "bar". I need to be able to access both the "key" and "value" in an .each() loop.
I could use nested arrays such as var pageBreaks = [[900, "foo"], [600, "bar"]]; but I think it is very ugly especially trying to get users of my plugin to adopt the format.
An object is possible:
var pageBreaks = {
900 : "foo",
600 : "bar",
}
But it would be more messy to loop through and it would be easier to process if I keep it ordered greatest to least which isn't guaranteed in an object (right?).
So what would be the best data structure to handle this array of "value-value" pairs with both sides accessible in a loop?
A Map could be a good approach, assuming you have access to the newer JS syntax:
let pageWidths = new Map();
pageWidths.set(900, "foo");
pageWidths.set(600, "bar");
// ordered
const orderedWidths = new Map([...pageWidths.entries()].sort());
// iteration with forEach
orderedWidths.forEach(console.log.bind(console));
// iteration with for...of
for (var [key, value] of orderedWidths) {
console.log(key, value);
}
Even though it will not maintain order on its own (no built-in JS data structure will do that for you*), Maps are fairly easy to work with and easy to accomplish your goals with.
* unless you are using arrays with the pageWidth integer as the index, but even that would require a bit of work to filter out undefined elements between them
You can use a Hashmap data structure to achieve your goal here. I wrote (am writing) a data structure library in JavaScript, and have already finished the Hashmap portion if you're interested in using it.
You're able to store key-value pairs (or in your case, value-value pairs) and iterate through them in the same order that they were inserted.
const Hashmap = require('node-needle').Hashmap;
var map = new Hashmap();
map.put(900, "foo");
map.put(600, "bar");
// ...
// Iterate through map - insertion order is kept
for(var it = map.iterator(); it !== null; it = map.next()){
console.log(it); // 900 -> 600 -> ...
console.log(map.get(it)); // "foo" -> "bar" -> ...
}

How to sort object by number of properties in Javascript? [duplicate]

This question already has answers here:
Sort Keys in Javascript Object
(2 answers)
Closed 2 years ago.
I have an object
{
"William_Hill":{},
"bet365":{},
"royal panda":{"pay":0},
"karamba":{"roller":0,"braned":0,"pay":0},
"betfred":{"braned":0}
}
and want to sort it by the nuber of properties, so the result shoul look like this and it must be an object
{
"William_Hill":{},
"bet365":{},
"betfred":{"braned":0},
"royal panda":{"pay":0},
"karamba":{"roller":0,"braned":0,"pay":0}
}
I have sorted it like this one but the result is an array
var keysSorted = Object.keys(resultArray).sort(function(a,b){
return Object.keys(resultArray[a]).length-Object.keys(resultArray[b]).length;
});
Until ES2015, JavaScript object properties had no defined order. They do now, but that order isn't respected by for-in or Object.keys, only by new features like Object.getOwnPropertyNames. It's also more complicated than it seems and of very limited use.
There's almost never any good reason to try to put an object in a specific order. If you want order, use an array, or in ES2015 use a Map (see below).
But that said, in ES2015, with property names like yours, it's possible: The only way to set the order of the properties in an object is to determine the order in which they're created, which (for properties that aren't array indexes) is the order they'll be in. So you can do this by getting all the property names, determining the order you want them in, and then creating a new object, adding the properties in that order.
Beware, though, that certain classes of property names (what we call "array indexes") are sorted differently. None of your property names meets the definition, but (say) "123" would.
Here's an example of doing it, if you really want to (I've happily used ES2015's arrow functions, since this only works in ES2015):
// The object
let obj = {
"William_Hill":{},
"bet365":{},
"royal panda":{"pay":0},
"karamba":{"roller":0,"braned":0,"pay":0},
"betfred":{"braned":0}
};
// Get its names, sort them by how may sub-props there are
let names = Object.keys(obj);
names.sort((a, b) => Object.keys(obj[a]).length - Object.keys(obj[b]).length);
// Get the new object
let newObj = names.reduce((acc, name) => {
acc[name] = obj[name];
return acc;
}, {});
// Show its property names
console.log(Object.getOwnPropertyNames(newObj));
Naturally, that can be shorter, I've kept the steps separate for clarity. Similarly, I go back and forth on that use of reduce, you could do the last bit like this instead:
// Get the new object
var newObj = {};
names.forEach(name => {
acc[name] = obj[name];
});
Here's an example using a Map instead, which offers much less complicated ordering of entries (no worries about "array indexes"):
// The object
let obj = {
"William_Hill":{},
"bet365":{},
"royal panda":{"pay":0},
"karamba":{"roller":0,"braned":0,"pay":0},
"betfred":{"braned":0}
};
// Get its names, sort them by how may sub-props there are
let names = Object.keys(obj);
names.sort((a, b) => Object.keys(obj[a]).length - Object.keys(obj[b]).length);
// Get the map
let map = new Map();
names.forEach(name => {
map.set(name, obj[name]);
});
// Show its property names
console.log(Array.from(map.keys()));
Dict object in Javascript cannot guarantee the element order.
So maybe you can only use an array to handler the sorted result.
Reference: Does JavaScript Guarantee Object Property Order?

What is difference between Map and Set?

JavaScript Map and Set objects are both iterable objects. Both store object by [key, value] pair.
When to use which? What is the difference between them?
Provided you are talking about the ES6 types, they aren't the same data structure even though the Set might be implemented with a Map.
Your definition of Map is right, but a Set is a collection of unique values, unlike an array which can have duplicates.
var array = [1, 2, 3, 3];
var set = new Set(array); // Will have [1, 2, 3]
assert(set.size, 3);
var map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
map.set('C', 3);
map.set('a', 4); // Has: a, 4; b, 2; c: 3, C: 3
assert(map.size, 4);
Summary:
Use a Set when your dataset needs to be composed of unique values
Use a Map when you have pairs of associated data. You map the keys to the values
Example Set:
There is a meeting with people coming from different organizations. Some people come from the same organization. We need to compose a list all the different organzations. For this we can use a set since we only want to include every organization once:
const organization = new Set();
organization.add('org1');
organization.add('org2');
organization.add('org3');
organization.add('org1');
organization.add('org3');
organization.add('org1');
for(let org of organization){
console.log(org);
}
Example Map:
We have a pack of dogs and want to assign an age to each dog. We want to map the unique name of each dog to the age of the dog:
const dogs = new Map([['fluffy', 10], ['barkie', 13]]);
dogs.forEach((value, key) => console.log(key, value));
How is Map different from an Object?
An Object is also a collection of key value pairs and can fulfill often the same purpose as a Map can (which is creating key-value pairs). However, there are some key differences between a Map and an Object:
Map is built in Iterable, this allows it to use the for of loop or its implementation of the forEach() method which an plain JS Object cannot use.
Map has some nice built in methods on its prototype which makes working with it very nicely. Because al Objects inherit from Object.prototype is has access to more useful methods. For example, the size() method on Map returns the number of keys in the Map.
var obj = {};
obj.name= "Anand Deep Singh";
console.log(obj.name); //logs "Anand Deep Singh"
similarly in ES6, we can use regular object.
var map = new Map();
map.set("name","Anand Deep Singh");
console.log(map.get("name")); //logs "Anand Deep Singh"
But noticeable thing is a Map isn’t created with the literal object syntax, and that one uses set and get methods to store and access data.
It has a has method to check whether the key exists in the object or not, delete method to delete the object and clear method to clear the entire object.
Set is a unique list of values. It’s simply a unique list.
var set = new Set(["a", "a","e", "b", "c", "b", "b", "b", "d"]);
console.log(set); //logs Set {"a", "e", "b", "c", "d"}
A Set can’t be accessed like an array, and it provides the same methods as a Map.
There are two main data structures:
Objects:
are used for storing keyed collections.
Arrays:
are used for storing ordered collections.
But that’s not enough for real life. That’s why Map and Set also exist.
Map:
is a collection of keyed data items, just like an Object. But the main difference is that Map allows keys of any type.
For instance:
let map = new Map();
map.set('1', 'str1'); // a string key
map.set(1, 'num1'); // a numeric key
map.set(true, 'bool1'); // a boolean key
let hamid = { name: "Hamid" };
// hamid is the key for the map
map.set(hamid, 123); // an object key
Set
: is a special type collection – “set of values” (without keys), where each value may occur only once.
instance:
let set = new Set();
let hamid= { name: "Hamid" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
// visits, some users come multiple times
set.add(hamid);
set.add(pete);
set.add(mary);
set.add(hamid);
set.add(mary);
// set keeps only unique values
alert( set.size ); // 3
https://javascript.info/map-set
In Map(), keys can be of any type [String, number, object] except for
regular objects that must be strings.
The Set is a one-dimensional array with unique keys, while the Map is
a two-dimensional array with key-value pairs, where each key shall be
unique.
For Map(), we allow the use of other primitive types (including NaN). As a result, developers are able to link to other types of data.
A Set consists of a collection of unique values, while a Map is a pair of associated data when we map the keys to the values. Both Map and Set have similar methods, such as .has(), .get(), .delete(), and .size().
The difference is that a map has a key-value pair and two dimensions. It is possible to convert both 2D arrays and arrays to sets.
To summarise, Map is used for key-value pair collections, while Set is used for unique value collections.
if you have unique values that you want to put in the set, then you should use Set as it is made for unique values
else if you don't have any issues with 2 or more same values then you should prefer Map

Categories