I'm fetching relational data from my sql database. This data has an identifier (unique/primary key). Sample:
[
{id:1, value:“test“, cat:2},
{id:2, value:“test“, cat:3},
{id:3, value:“test“, cat:4}, ...
]
As proposed on many sites, including Stackoverflow, you may use an array's find function to access the elements by their id:
Find object by id in an array of JavaScript objects
Sample to fetch the value of object with id 3:
SomeVal = myArray.find(x => x.id === 3).value
However, I disagree with this approach. I don't see why you would search for an identifier, as you could just simply access the element directly via id, which really is the idea to use an identifier.
You may argue that the array's find function will just do the job as it's superfast, but that's not good enough in my case as performance has highest priority.
As result, I currently "misuse" the index of the array as identifier number to access the elements directly. Sample:
SomeVal = myArray[3].value
However, I am looking for a more feasable approach in javascript/typescript to access the elements by id directly.
I do not need to create/update/delete the elements, I solely need to access them once fetched.
I must not use arrays at all if another data structure is better for this.
I am NOT proposing to replace an array’s field search function such as find with the direct index access. And the core of the discussion shall not be which array element is accessed by 3, as this is a simple example. The main idea of the question is if it’s feasible to use the index if an array like it was the identifier, hence to use index which directly correlates with the id field.
However, I disagree with this approach. I don't see why you would search for an identifier, as you could just simply access the element directly via id, which really is the idea to use an identifier.
You can't directly access by ID with the structure you've shown. You need a different structure to do that.
As result, I currently "misuse" the index of the array as identifier number to access the elements directly. Sample:
SomeVal = myArray[3].value
That doesn't access the entry with id: 3. It accesses undefined (with your sample data). Indexes start at 0, not 1. Moreover, unless the underlying system guarantees it, you can't rely on the entries being in id order with no gaps, etc.
For access by id, convert the array to a Map or an object. Then you can reuse that map or object and benefit from the sublinear lookup time on the map key / object property name.
Map (I'd recommend this):
// Note that I've put them out of order to demonstrate we're really getting by id
const array = [
{id:2, value:"test", cat:3},
{id:1, value:"test", cat:2},
{id:3, value:"test", cat:4},// ...
];
const map = new Map(array.map(entry => [entry.id, entry]));
console.log(map.get(1));
Object:
// Note that I've put them out of order to demonstrate we're really getting by id
const array = [
{id:2, value:"test", cat:3},
{id:1, value:"test", cat:2},
{id:3, value:"test", cat:4},// ...
];
const obj = Object.fromEntries(array.map(entry => [entry.id, entry]));
console.log(obj[1]);
The reason you have to use find in this case is because you have an array of objects. So you cannot directly access the id key.
If you want to access the id key directly, then you need to convert your array to an object.
In my opinion, there are two approaches to do that.
Using a data structure algorithm such as HashTable to find the element easily and in fast time.
Create an object that id is key and its value is the entity object. something like this:
const myArray = [...];
const cachedMyArray = cacheData(myArray);
const anItem = cachedMyArray[3]
// Caching data to find elements fastest
interface MyArray {
...
}
function cacheData(array: MyArray[]) {
const cached = {};
array.forEach((item: MyArray) => {
cached[item.id] = item;
});
return cached;
}
Related
I know variations of this question have been asked but bear with me.
I have the following array containing objects (which are routines):
const PREMADE_ROUTINES = [
{
itemIds: ['makebed', 'drinkwater', 'quickstretch', 'hotshower', 'brushteeth', 'smallsnack', 'getdressed', 'todolist', 'declutterinbox',],
routineDuration: DEFAULT_ROUTINE_ITEMS.getItemsFromIds(PASS THIS OBJECTS itemIds HERE)
}
]
How could I access the itemIds in this case within each of the objects in the PREMADE_ROUTINES array?
In this case, I would need to pass the objects itemIds as an argument to the function. I could do this if the object wasn't in an array with a get(). However, I don't know how to do it in this case. Any ideas?
Ideally, I would like to simply access the routineDuration key by looping and simply accessing it.
In your particular context the best solution is to break away the itemIds declaration. This is so you don't run into this issues when populating it later on.
const DEFAULT_ROUTINES = [
...
]
const PREMADE_ROUTINES = [ {
itemIds: DEFAULT_ROUTINES,
routineDuration: yourFunctionHere(DEFAULT_ROUTINES)
]}
I notice your data structure is a bit complex. It might be worth refactoring it, and introducing a couple utility / filtering methods to make your life easier in the future.
You could create a method returning your routines object. That way it'd be more reusable.
const createRoutine = (routines) => ({
itemIds: routines,
routineDurations: getRoutineDurations(routines)
});
const PREMADE_ROUTINES = [
createRoutine(['makebed', 'drinkwater', 'quickstretch']),
createRoutine(['hotshower', 'brushteeth', 'smallsnack']),
];
I'd like to return the third node (hello3.com) of the key hello.com in javascript object.
nodes = {
"hello.com":
{
id:"hello1.com",
id2:"hello2.com",
id3:"hello3.com"
}
}
I know that I can fetch all the key/values like this:
newobject = nodes["hello.com"];
but how would I get the third. I'm aware that you can't count on the order in an object. If not, can I pull just the third by maybeb id3.
You answered your own question when you said that you can't count on the properties of an object to be in any certain order. If your properties are sequential in nature (your properties were counting up in your example), then I would suggest trying to use an Array.
nodes = {
"hello.com": [
"hello1.com",
"hello2.com",
"hello3.com"
]
};
In the above example, you would access the 3rd property with
nodes["hello.com"][2]
The double bracket notation is because "hello.com" is in quotes to allow a . in the name. If the key didn't require quotes, like helloCom as an example, you could use
nodes.helloCom[2]
Beyond this, if you name your keys sequentially, then you can impose an "order". It's not that any property is literally before or after another, but rather that you have informed yourself of what order YOU intend them to be.
You can try this,
nodes = {
"hello.com": {
id: "hello1.com",
id2: "hello2.com",
id3: "hello3.com"
}
}
console.log(nodes["hello.com"]["id3"]);
Use:
nodes['hello.com'].id3 or nodes['hello.com']['id3']
Both are corrent way to get id3 data from given object
BY INDEX :
About accessing by index, you can not achieve it directly. the closest you can get is array of keys but that also do not guarantee the order of keys returned. See this answer provided on other thread.
for (var i in nodes["hello.com"]) { console.log(i);//logs id,id2,id3 };
BY NODENAME:
nodes["hello.com"] returns object. You can use key to access the value by
1) using dot notation:
nodes["hello.com"].id3
2) or by bracket notation
nodes["hello.com"]["id3"]
Try one of the following expressions
nodes["hello.com"]["id3"]
or
nodes["hello.com"].id3
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>
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.
I have a js 'associative' array, with
array['serial_number'] = 'value'
serial_number and value are strings.
e.g. array['20910930923'] = '20101102'
I sorted it by value, works fine.
Let's say I get back the object 'sorted';
Now I want to access the first KEY of the 'sorted' array.
How do I do it? I can't think I need an iteration with
for (var i in sorted)
and just stop after ther first one...
thanks
edit: just to clarify, I know that js does not support associative arrays (that's why I put it in high commas in the Title).
2021 Update
Since ES6, properties with string keys are enumerated in insertion order. Here's a nice summary. My original answer from 2010 was correct at the time and is preserved below:
Original answer
JavaScript object properties are specified to have no order, much though many people wish it were different. If you need ordering, abandon any attempt to use an object and use an Array instead, either to store name-value objects:
var nameValues = [
{name: '20910930923', value: '20101102'},
{name: 'foo', value: 'bar'}
];
... or as an ordered list of property names to use with your existing object:
var obj = {
'20910930923': '20101102',
'foo': 'bar'
};
var orderedPropertyNames = ['20910930923', 'foo'];
Try this:
// Some assoc list
var offers = {'x':{..some object...}, 'jjj':{...some other object ...}};
// First element (see attribution below)
return offers[Object.keys(offers)[0]];
// Last element (thanks to discussion on finding last element in associative array :)
return offers[Object.keys(offers)[Object.keys(offers).length - 1]];
Actually JavaScript doesn't support associative arrays, so you can't loop through it in an implied order (e.g. you can't access it via the indexer property array[0] won't access the first element in your object). The syntax is what makes it look like it does, but in reality it doesn't. So you have no "Order" to your objects.
http://www.hunlock.com/blogs/Mastering_Javascript_Arrays
Javascript does not have, and does not
support Associative Arrays. However…
All arrays in Javascript are objects
and Javascript's object syntax gives a
basic emulation of an associative
Array. For this reason the example
code above will actually work. Be
warned that this is not a real array
and it has real pitfals if you try to
use it. The 'person' element in the
example becomes part of the Array
object's properties and methods, just
like .length, .sort(), .splice(), and
all the other built-in properties and
methods.
Just thinking off the top of my head, but could you have another array with the key value pairs swapped?
So the answer would be arrayKeyValueReversed['20101102'] = '20910930923';
When you sort the array, use the first item (array[0]) as the key to get the value in the arrayKeyValueReversed.