Add attribute to JS object specific place - javascript

I want to add attribute to a JS object, but in a custom place, After a given attribute.
var me = {
name: "myname",
age: "myage",
bday: "mybday"
};
me["newAt"] = "kkk"; //this adds at the end of the object
Is there a way to specify the object (me), an attribute(age) in it and add a new attribute(newAt) right after the specified one? A better way than doing string operations?
var newMe = {
name: "myname",
age: "myage",
newAt: "newAttr",
bday: "mybday"
}
UPDATE: (Since people are more focused on why I'm asking this than actually answering it)
I'm working on a drawable component based on user input - which is a JS object. And it has the ability to edit it - so when the user adds a new property based on "add new node" on the clicked node, and I was thinking of adding the new node right after it. And I want to update the data accordingly.

JavaScript object is an unordered list of properties. The order is not defined and may vary when using with an iterator like for in. You shouldn't base your code on the order of properties you see in debugger or console.

JavaScript objects do, as of ES2015, have an order to their properties, although that order is only guaranteed to be used by certain operations (Object.getOwnPropertyNames, Reflect.ownKeys, etc.), notably not for-in or Object.keys for legacy reasons. See this answer for details.
But you should not rely on that order, there's no point to it, it's more complicated than it seems initially, and it's very hard to manipulate (you basically have to create a new object to set the order of its properties). If you want order, use an array.
Re your edit:
I'm working on a drawable component based on user input - which is a JS object. And it has the ability to edit it - so when the user adds a new property based on "add new node" on the clicked node, and I was thinking of adding the new node right after it. And I want to update the data accordingly.
The best way to do that is, if you want a specific order, keep the order of keys in an array and use that to show the object.
While you could use ES2015's property order for it, to do so you'd have to:
Require your users use a truly ES2015-compliant browser, because this cannot be shimmed/polyfilled
Destroy the object and recreate it adding the properties in the specific order you want each time you add a property
Forbid properties that match the specification's definition of an array index
It's just much more work and much more fragile than keeping the order in an array.

The simplest solution I could find was to iterate through the keys of the parent and keep pushing them to form a clone of the parent. But to additionally push the new object if the triggered key is met.
var myObj = {
child1: "data1",
child2: "data2",
child3: "data3",
child4: "data4"
};
var a = (function addAfterChild(data, trigChild, newAttribute, newValue) {
var newObj = {};
Object.keys(data).some(function(k) {
newObj[k] = data[k];
if (k === trigChild) {
newObj[newAttribute] = newValue;
}
});
return newObj;
})(myObj, "child3", "CHILD", "VALUE");
document.getElementById("result").innerHTML = JSON.stringify(a);
<p id="result"></p>

Related

Prototyping the accessors of ALL POSSIBLE sub-properties

This is the code I'm starting with to prototype goal into the Creep class:
Object.defineProperty(
Creep.prototype,"goal",{
set :function(value){
this.memory.goal= value.id;
},
get :function() {
return Game.getObjectById(this.memory.goal);
},
}
)
Now let's suppose I want Creep.goal not to contain a single value, but multiple values instead, and let every single sub-properties of Creep.goal have the foresaid accessors.
(So I can easily store multiple game objects into the creep's memory)
These properties are meant to be added at runtime, so I do not know how many there will be nor their names, thus I can't simply copy-paste this code once for every property I'd like there to be.
How should I proceed in order to define the accessors of all possible properties-to-be of an object ?
----- SOLUTION -----
So I was suggested to use a Proxy for this. It was a completely new concept to me and I've hit a lot of walls, but I got something to work like I wanted !
// Prototyping goal as a proxy
Object.defineProperty(
Creep.prototype,"goal",{
get :function()
{return new Proxy(this.memory.goal, objectInMemory) }
}
)
// Proxy's Handler (my previous accessors)
const objectInMemory= {
set(goal, property, value){
goal[property] = value.id;
return true;
},
get(goal, property){
return Game.getObjectById(goal[property]);
},
}
Not exactly sure what you're aiming for, but if the properties are truly dynamic and have to be evaluated at runtime there are Proxy objects that are supported by Screeps' runtime.
What this does is it allows you to programmatically intercept all of the messages for an object, including accessing properties.

JavaScript - Storing objects with unique key ?

I have an JavaScript object which is being pushed to a global object.
var storedElement = {
element: currentElement,
parentElement: parentElement,
elementChild: currentChild
}
storedElement is being pushed to a global array called.
pastLocations = []
I'm essentially trying to keep a history of the locations an element has been to. To do this I'm wanting to store these properties into the global array. If the same element already exists and has the same parent in the global then I dont want to push, but if the parent is different then push.
Is there a way I can put a unique key with item so I quickly and effectively get access to this element in the object. At the moment I currently have several for each loops to get the data from the object but this inst a practical approach. As Ideally I want 1 function to push to the global and to retrieve an element.
If I was to provide a unique keys for each object, how would I would know what key it is based of just knowing the element ?
In Javascript, an array [...] stores sequential values, preserving their order, and provides fast access if you know the index.
An object or dictionary {...} stores values along with a key, without preserving their order, and provides fast access if you know the key.
If you only need to store elements with distinct 'parent', you can use an object, using the parent as key. If you also need to browse them in order, your best bet is to use both an array and an object:
storedElements = []
storedByParent = {}
What you store in each depends on your application requirements. You may store a copy of the object:
newEl = {element: ..., parent: parentElement, ...}
storedElements.push(newEl)
storedByParent[parentElement] = newEl
Or you may store an index into the array:
storedElements.push(newEl)
storedByParent[parentElement] = storedElements.length - 1
Or you may store a simple boolean value, to just keep track of which parents you have seen:
storedElements.push(newEl)
storedByParent[parentElement] = true
This latter use of an object is usually known as 'set', because it's similar to the mathematical object: even if you call mySet[12] = true a hundred times, the set either contains the element 12, or it does not.

Understanding lists in JavaScript

I am reading through Eloquent JavaScript and have been stuck trying to understand lists for about two days so I figured I would finally ask a question. The example they give in the book is:
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
Now I think I understand the example... There is a list object and it has properties value and rest. Then, rest has properties of value and rest, etc... However, I don't understand what rest is or even stands for. Does the rest property contain an object? So, list.rest.value would == 2? How is this useful? Some ways I could see this as useful are having a list Car, with prop engine, gauge, etc, with further properties of accelerate, brake, low fuel... How would something like this be achieved?
I do apologize for the "all overness" of this post, I don't exactly know what to ask or how to phrase it. It seems like the book only explained objects and properties, but never actually having objects as an objects property.
Thank you all in advance, and if you need any clarification or more info I will try to provide it.
This code simply uses JavaScript Object Notion to define an object named list.
// Would simply define an empty object.
var list = {};
Now you can add some properties to the object.
// Would define an object with a single property: `value`.
var list = {
value: 1
};
Using nested object declarations, you can give the list object child objects as well:
var list = {
value: 1,
rest: {}
};
Now list.rest is an empty object. You can fill that out by adding some properties:
var list = {
value: 1,
rest: {
value: 2
}
};
And your nesting can continue ad-infinitum. The object in your original post, the following is possible:
console.log(list.value); // 1
console.log(list.rest.value); // 2
console.log(list.rest.rest.value); // 3
It's important to understand that this in no way creates a class or includes any additional methods with the object. It seems to be structured as a linked list but provides no functionality to add/remove/modify (except by directly modifying the original object).
In the example above the list variable is an associative array. This is JavaScript's version of an "object". While the property list.value ends up being typed as an integer, the property list.rest is typed as a nested associative array. The properties themselves can be any valid type. Many jQuery plugins are coded where the properties themselves are actually delegate functions.
The object you have described above in the example does not seem to me to be terribly useful beyond being an example of how this kind of object can contain references to other objects. However, when you begin applying this in an "object oriented" concept (keep in mind that it is not truly object oriented), it becomes more useful. You can then create your own "namespace" with properties, functions and delegates that can be re-used time and again.
Thank you all for your information. I don't know if there is a best answer selection on this site or not, but I really do appreciate the help Justin, Joel, and Evan. I think the main part I was confused about is just practical application for real applications. I have messed around a little bit and came up with this and have a much better basic understanding now:
var car = {
engine: {
turn_on: "Turned engine on",
turn_off: "Turned engine off",
desc: {
size: "V6",
year: 2000
}
},
fuel: {
level: 55
}
};
function CheckFuel(fuel){
if(fuel > 50){
console.log("In good shape");
}
else{
console.log("We should fuel up");
}
}
console.log(car.engine.turn_on);
console.log(car.engine.turn_off);
console.log(car.engine.desc.size);
console.log(car.engine.desc.year);
CheckFuel(car.fuel.level);
Now time to practice iterating through. Thanks again!
This is an implementation of a linked list. Each node in the list has a reference to the next node. 'Rest' is an object (the next node in the list) that also contains every other node in the list (via it's rest property).
The first value in the list would be list.value;. The second value in the list would be list.rest.value;. The items in the list can be shown as:
item1 = list;
item2 = list.rest;
item3 = item2.rest;
This continues until itemX.rest is null.
These two functions could be used to manage the list and may help you understand how iterating through it would work:
function addToList(item)
{
if(!list)
{
list = item;
return;
}
var temp = list;
while(temp.rest)
{
temp = temp.rest;
}
temp.rest = item;
}
function printList()
{
var temp = list;
while (temp)
{
print temp.value; //i'm not sure what the javascript print function is
temp = temp.rest
}
}
The add function would be called like this: addToList({ value:10, rest:null });

How to remove last key:value pair in JavaScript

I am very new to JavaScript and I am trying to figure out how to set a function to remove the last key:value pair to the right, much like array.pop for an array. This is an assignment I am working on. It seems confusing to me because, from my limited understanding of JS their is no specific order in a list of properties in an object. If anyone has any insight I would appreciate it. Here is the object:
var array = {length:0, size:big, smell:strange};
this is where I have started to go, but just having a hard time completing the function:
array.pop = function() {
//...
};
Ultimately I would like it to turn out like this:
array = {length:0, size:big};
Thanks in advance.
Objects do not have any defined order of properties so there is no "last" property. You have to remove a property by name, not position.
You can, of course, iterate over the properties and inspect them as you iterate and decide whether you want to delete any given property by looking at its name. Some javascript implementations will preserve the order that properties were added, but that is specifically not guaranteed by the ECMAScript specification so it cannot be relied upon.
This will work
const car = {
color: 'blue',
brand: 'Ford'
}
let keys = Object.keys(car)
delete car[keys[keys.length-1]]
console.log(car)
This answer is good for those situtations where the key is dynamically generated numbers like 0,1,2,3,4 etc
const myObject = {
0: 'somestring',
1: 42,
2: false
};
delete myObject[`${Object.keys(myObject).length-1}`]
console.log(myObject);
output:
Object { 0: "somestring", 1: 42 }
this one line logic may not good when key is a string. So, carefully use it.
The snippet below demonstrates that "objects have no order", and an [inefficient] workaround: use an array alongside of the object, to store the order that the properties were added to the object.
Click to add random properties, and note the order that they appear below.
In CodePen (or on my webserver) the properties seem to be stored sorted numerically (even though they're stored as strings).
However, in the snippet below they seem to be ordered randomly.
Neither are the order that the properties are added.
It should be noted:
Unlike what common belief suggests (perhaps due to other programming languages like delete in C++), the delete operator has nothing to do with directly freeing memory. Memory management is done indirectly via breaking references.
More info: delete operator and Memory Management.
var obj={}, // object to store properties (keys) and values
props=[]; // array to store property names
add.onclick=function(){
var prop=rnd(), val=rnd(); // get 2 random numbers
obj[ prop ] = val; // add property & value → object
props.push( prop ); // add property name → array
updateInfo(); // display object
}
del.onclick=function(){
var lastProp=props.pop(); // get/remove last property name in array
delete obj[ lastProp ]; // remove property
updateInfo(); //display object
}
function rnd(){return Math.floor(Math.random()*1E5);} // random 0-99999
function updateInfo(){ // show pretty object 😘
info.innerHTML=JSON.stringify(obj).replace(/[\{\}]+/g,"").replaceAll(',','<br>');
}
<button id='add'>add new property</button>
<button id='del'>delete last added</button>
<div id='info'></div>

JavaScript array won't fill up properly

I'm making a Google Chrome Extension that uses context menus as its main UI. Each menu item triggers the same content script, but with different parameters. What I basically did is store every item (and its corresponding data) in the form of a JSON object that has the following form :
{name, parent_id, rule_number, meta_array[], childCount}
name, child_count and parent_id are used to create the hierarchy when the context menus are built. The data that's passed to the script is rule_number (int) and meta_array (array of strings). All of these objects are stored into an array called indexData[].
When a menu item is clicked, the id provided is just used as an index in the "indexData" array to get the right data and pass it to the script.
For example:
// Iterates through the objects
for(var j = 0; j < objectsArray.length; j++) {
// Context menu created with unique id
var id = chrome.contextMenus.create({
"title": objectArray[j].name,
"onclick": injectScript,
"parentId": objectsArray[j].parent_id });
// Stores the objects at the corresponding index
indexData[id] = objectsArray[j]; }
Now, there was a particular large set of data that comes back often. Instead of listing every single of these elements every time I wanted them as part of my menu, is just added a boolean parameter to every JSON object that needs this set of data as its children. When the menus are created, a function is called if this boolean is set to true. The script then just iterates through a separate list of objects and makes them children of this parent object. The created children even inherit certain things from the parent object.
For example, if a parent object had a meta_array like such ["1", "2", "3", "4"], its children could all look like so ["1", "2", custom_children_data[3], "4"].
The problem is that this last part doesn't work. While the children are created just fine and with the right name, the data that's associated with them is wrong. It's always going to be the data of the last object in that separate list. This is what the function looks like:
// Iterate through children list
for(var i = 0; i < separateList.length; i++){
// Just copying the passed parent object's data
var parentData = data;
var id = chrome.contextMenus.create({
"title": separateList[i].name, // Get item [i] of the children list (works fine)
"onclick": injectScript,
"parentId": parentId // Will become a child of parent object
});
// Trying to change some data, this is where things go wrong.
parentData.meta[2] = separateList[i].meta;
// Save in indexData
indexData[id] = parentData; }
On the loop's first iteration, parentData.meta[2] gets the right value from the list and this value is thereafter saved in indexdata. But on subsequent iterations, all the values already present in indexData just get swiped away and replaced by the latest data being read from the list. When the last value is read, all the newly added elements in indexData are therefore changed to that last value, which explains my problem. But why on earth would it do that ? Does Java somehow treat arrays by address instead of value or something in this case ?
Maybe I'm missing something really obvious, but after many attempts I still can't get this to work properly. I tried to be as specific as possible in my description, but I probably forgot to mention something, so if you want to know anything else, just ask away and I'll be happy to provide more details.
Thanks.
The problem would be indexData[id] = parentData where you are making indexData[id] a reference to parentData, and then modifying parentData on the next iteration of your loop.
Since parent data is not a simple array (It contains at least one array or object), you cannot simply use slice(0) to make a copy. You'll have to write your own copy function, or use a library which has one.
My guess is that this is where your problem lies:
// Just copying the passed parent object's data
var parentData = data;
This does not, in fact, copy the data; rather, it creates a reference to data, so any modifications made to parentData will change data as well. If you're wanting to "clone" the data object, you'll have to do that manually or find a library with a function for doing so.

Categories