Find element in Object Array by key value - javascript

What is the best structure solution for find element(Object) in Object Array by value of one of the keys.
For example we have array:
var someArray =
[
{id:17, color:'black', width:50},
{id:34, color:'red', width:150},
{id:49, color:'gree', width:10}
]
and we need to find an Object with id-key = 34.
And each time we will have to do the loop to find the object.
I thought about restructuring and having object instead of array like so:
var someObject =
{
17: {id:17, color:'black', width:50},
34: {id:34, color:'red', width:150},
49: {id:49, color:'gree', width:10}
}
Now we can do it in one step someObject[34], BUT what if we want to keep the order?
Thanks in advance.

I thought about restructuring and having object instead of array
Yes, that's fine.
BUT what if we want to keep the order?
I tend to use an extra array that contains the keys in the correct order, like
var order = [17, 34, 47];
To loop them, you'd use
for (var i=0; i<order.length; i++) {
… someObject[order[i]] …
}

You should prefix the id to avoid naming collisions, like this:
var database =
{
'row_17' : {id:17, color:'black', width:50},
'row_34' : {id:34, color:'red', width:150},
'row_49' : {id:49, color:'gree', width:10}
};
At this point you can query the object with this code:
function retrieve(database, id)
{
id = 'row_' + id;
if (!database.hasOwnProperty(id))
{
return null;
}
return database[id];
}
The persist function would be:
function persist(database, obj)
{
database['row_' + obj['id']] = obj;
}
If you need to keep an order you have first to understand what order you are talking about.
Of the id? Of the insert? Or of an arbitrary property?
There are solutions to all of these by filtering (extracting the items and putting them into a separate array that would be the query result or with additional fields or structures.
EDIT: how to keep insertion order
You'll need an array that keeps track of order:
database._ordered = [];
On insert, push the item there, too:
database._ordered.push(obj);
Now you can pick single items by key and all items ordered.
There is no way you can have an order in an object, it's simply the wrong tool for that.
The row_ prefix is recommended to avoid naming collisions with methods and whatnot. An object should hold properties and methods, if you use it like a dictionary at least prevent interference by prefixing.

You can use function findWhere from underscore library, in your case it would be like this:
_.findWhere(someArray, {id: 34})

Related

Is it bad to have "number strings" as object keys?

I have two APIs to work with and they can't be changed. One of them returns type like this:
{
type: 25
}
and to other API I should send type like this:
{
type: 'Computers'
}
where 25 == 'Computers'. What I want to have is a map of numeric indices to the string value like this:
{
'1': 'Food',
'2': 'Something',
....
'25': 'Computers'
....
}
I am not sure why, but it doesn't feel right to have such map with numeric value to string, but maybe it is completely fine? I tried to Google the answer, but couldn't find anything specific. In one place it says that it is fine, in another some people say that it's better not to have numeric values as object keys. So, who is right and why? Could somebody help me with this question?
Thanks :)
There's nothing wrong with it, but I can understand how it might look a little hinky. One alternative is to have an array of objects each with their own id that you can then filter/find on:
const arr = [ { id: 1, label: 'Food' }, { id: 2, label: 'Something' }, { id: 25, label: 'Computers' } ];
const id = 25;
function getLabel(arr, id) {
return arr.find(obj => obj.id === id).label;
}
console.log(getLabel(arr, id));
You can use the Map object for this if using regular object feels "weird".
const map = new Map()
map.set(25, 'Computers');
map.set(1, 'Food');
// then later
const computers = map.get(25);
// or loop over the map with
map.forEach((id, category) => {
console.log(id, category);
});
Quick Update:
As mentioned by others, using objects with key=value pairs is OK.
In the end, everything in javascript is an object(including arrays)
Using key-value pairs or Map has 1 big advantage( in some cases it makes a huge difference ), and that is having an "indexed" data structure. You don't have to search the entire array to find what you are looking for.
const a = data[id];
is nearly instant, whereas if you search for an id in an array of objects...it all depends on your search algorithm and the size of the array.
Using an "indexed" object over an array gives much better performance if dealing with large arrays that are constantly being updated/searched by some render-loop function.
Map has the advantage of maintaining the insertion order of key-value pairs and it also only iterates over the properties that you have set. When looping over object properties, you have to check that the property belongs to that object and is not "inherited" through prototype chain( hasOwnProperty)
m = new Map()
m.set(5, 'five');
m.set(1, 'one');
m.set(2, 'two');
// some other function altered the same object
m.__proto__.test = "test";
m.forEach((id, category) => {
console.log(id, category);
});
/*
outputs:
five 5
one 1
two 2
*/
o = {};
o[5] = 'five';
o[1] = 'one';
o[2] = 'two';
// something else in the code used the same object and added a new property
// which you are not aware of.
o.__proto__.someUnexpectedFunction = () => {}
for (key in o) {
console.log(key, o[key]);
}
/*
Output:
1 one
2 two
5 five
someUnexpectedFunction () => {}
*/
Map and objects also have 1 very important advantage(sometimes disadvantage - depending on your needs ). Maps/objects/Sets guarantee that your indexed values are unique. This will automatically remove any duplicates from your result set.
With arrays you would need to check every time if an element is already in the array or not.

Get the hierarchical order of a custom array list based on specific rules

I am wanting to get the hierarchical order from the below array list.
var data = [
['','dog'],
['dog','cat'],
['dog','fish'],
['dog','ark'],
['dog','folder'],
['cat','blue'],
['cat','pencil'],
['cat','mouse'],
['fish','stencil'],
['fish','bread'],
['fish','milk'],
['fish','love'],
['mouse','tv'],
['mouse','oil'],
['mouse','car'],
['milk','dough'],
['milk','butter'],
['car','truck']
];
The way to 'calculate' this is to start at the first array of
['','dog']
Note: I will refer to the 0 index ('') as 'parent' and 1 index ('dog') as 'child'
so we want to find the arrays that have 'dog' as the parent, and then check those arrays to see if their children have any parents, if not, continuing on to the next one. (really hope this makes sense)
Below is the result of the order for my above data set.
var result = ['dog', 'cat', 'blue', 'pencil', 'mouse', 'tv', 'oil', 'car', 'truck', 'fish', 'stencil', 'bread', 'milk', 'dough', 'butter', 'love', 'ark', 'folder'];
I have attempted this with some really bad while loops and .filters() to get arrays which contain the child as the parent index, but failed.
If anyone can give some insight into this or a way to return the end result I would be most appreciative as I cannot keep figuring this out manually.
Here, I think, is your algorithm.
Convert the source array to a nested object that uses pass-by-reference to efficiently associate parents and children.
Here's an ES6 implementation of that:
function pairsToIndex(pairs) {
return pairs.reduce((index, pair, i, list) => {
let [ parent , child ] = pair;
let parent_exists = index.hasOwnProperty(parent);
let child_exists = index.hasOwnProperty(child);
if(!parent_exists) {
index[parent] = { key: parent , children: [] };
}
if(!child_exists) {
index[child] = { key: child , children: [] };
}
// now, ensure that parent-child relationship is captured
let rel_captured = Boolean( index[parent].children.find((c) => c.key === child) );
if(!rel_captured) {
index[parent].children.push( index[child] );
}
return index;
}, {});
}
let data = [
['','dog'],
['dog','cat'],
['dog','fish'],
['dog','ark'],
['dog','folder'],
['cat','blue'],
['cat','pencil'],
['cat','mouse'],
['fish','stencil'],
['fish','bread'],
['fish','milk'],
['fish','love'],
['mouse','tv'],
['mouse','oil'],
['mouse','car'],
['milk','dough'],
['milk','butter'],
['car','truck']
];
let x = pairsToIndex(data);
console.log(x);
Run that code in your browser's dev console. As you dig through the object x, you'll notice that each of the "children" acts like a symlink to another top-level property of x. In effect, each "child" is a portal to somewhere else within that object. This use of pass-by-reference sets us up for step 2...
Serialize that object by recursively traversing it in a depth-first pattern and returning the key prop of each node.
So, here's a short impl of that:
function listNode(node) {
return Array.prototype.concat.apply( [node.key] , node.children.map(listNode) );
}
let out = listNode(x['']); // choosing the entry point to the hierarchy
console.log(out);
Note that I've had to use apply to avoid creating a bunch of nested, 1-element arrays (even though they would be ordered correctly). That's solvable with something like _.flatten, but this is a case where knowing a little more about the language obviates the need for an entire library.
Note also that listNode technically operates on a subtree of the hierarchy. I told it to start at the "root" node, which you've suggested is identified in the original dataset by the empty string. However, you could ask it to serialize a subset of the tree by passing it a different start point, e.g. listNode( x['cat'] ) or listNode( x['fish'] ).
Finally, this algorithm is infinitely recursive. Browsers will halt this script if you feed it a very large dataset. Other JS environments (like nodejs or react-native) will not do that.
At this point, out is an array that lists each of the values from the original data array, in the order you're hoping for.

Merge array into a single array

If this were .NET, I'd ask how to convert List<List<MyClass> to List<MyClass>. However, I'm not very good with javascript and don't know how to ask that as a question using Javascript terminology!
My javascript object comes through like
And is created as:
js_datasets.push({
"DataItem0": {
lzabel: "This",
data: [[1408710276000, null],[1408710276000, 15]]
},
"DataItem1": {
lzabel: "That",
data: [[1408710276000, null],[1408710276000, 15]]
},
});
js_datasets.push({
"DataItem22": {
lzabel: "And other",
data: [[1408710276000, null],[1408710276000, 5]]
},
"DataItem23": {
lzabel: "And lastly",
data: [[1408710276000, null],[1408710276000, 1]]
},
});
Each object is the same "type" (if it matters).
I'd like to create a single list but I am failing to do so. My efforts are
var myDataSet = []; //this is the results of what I want, ideally as a single list
for (var i = 0; i < js_datasets.length; i++) {
if (i==0) {
myDataSet.push(js_datasets[i]);
}
else {
myDataSet.concat(js_datasets[i]);//does nothing
myDataSet.join(js_datasets[i]);//does nothing
}
...more logic
As you can see with the above, I've tried using push, concat and join.
If I update the code to only use push (and never use concat and join) then I get all the values I want, but again, as an array within an array.
Using concat and join do not add to the list.
So, if we can assume the 12 items in the array (pictured) all contain 10 items, I'd like to have a single list of the 120 items!
How can I simply convert this multidimension array (is it multidimension) to a single dimension array.
This will be a bit complicated, as the items in your Array js_datasets are not Arrays, but a more generic Object. This means you can't assume the keys will be in order if you try to read them
Lets write some helper functions to account for this;
function dataItemCollectionToArray(o) {
var keys = Object.keys(o);
// assuming no non-DataItem keys, so next line commented out
// keys = keys.filter(function (e) {return e.indexOf("DataItem") === 0;});
keys.sort(function (a, b) { // ensure you will get the desired order
return +a.slice(8) - +b.slice(8);
});
return keys.map(function (e) {return o[e];});
}
Now you can loop over js_datasets performing this conversion
var myDataSet = [], i;
for (i = 0; i < js_datasets.length; ++i) {
// assuming no gaps, if you need to add gaps, also find min, max indices
// in `dataItemCollectionToArray`, and check them in each iteration here
myDataSet.push.apply(myDataSet, dataItemCollectionToArray(js_datasets[i]));
}
Please note that Object.keys and Array.prototype.map may require polifills if you wish to support old browsers, i.e. IE<=8
An easier solution however, may be to re-write how js_datasets is constructed so that the Objects you are pushing are more Array-like or indeed pushing true Arrays, perhaps with a couple extra properties so you know the offset for the first index. This would mean you can use flatten methods that you'll find around the internet

Return unique keys from JavaScript Array

I'm hoping my question is using the correct terminology...
Can someone explain to me how I can perform the following:
If I have an array consisting of:
Object { id=1498, brandName="Booths", quality="Standard"}
Object { id=1499, brandName="Booths", quality="Standard"}
How can I iterate throughout that array and return another array of distinct 'keys'?
Ultimately I want an array which would return something like:
[id,brandName,quality] (but the original array is going to return different keys at different times.
Have I made sense?
You can use Object.keys:
var a1 = [{ id:1498, brandName:"Booths", quality:"Standard"},
{ id:1499, brandName:"Booths", quality:"Standard"}],
a1Keys = a1.map(function(a){return Object.keys(a);});
//a1Keys now:
[['id','brandName','quality'],['id','brandName','quality']]
The keys method is described #MDN, including a shim for older browsers
var a = {"a": 1, "b": "t" };
var keys = new Array();
for(var o in a){
keys.push(o);
}
console.log(keys)

Handling JSON Object Array

I got a realy simple question:
Take a look at this JSON String:
this.objects = [{"pid":"2","x":"10","y":"10"}]; // only one i know
Now i would like to adress an object out of it like so:
this.objects.pid[2]
I know thats pointless in this case as you would access it like:
this.objects[0]
The thing is that i need to adress an object array in JSON by the object id and not the array index. Is there a nice approach to this?
Thanks!
function getObject(id, array) {
for (var i = 0; i < array.length; i++) {
if (array[i].pid == id) {
return array[i]
}
}
}
A function that takes your id and array and returns your object. Basically loop through the array and find the element with your id. This can be optionally cached for speed increase.
It doesn't need to be a single element array, so try this...
this.objects = {"pid":"2", "x":"10", "y":"10"};
And you can read it either of these ways:
this.objects.pid;
this.objects['pid'];
If you wanted multiple lists of x,y,etc. then try something like this:
this.objects = { "2": {"x": "10", "y": "10"} };
this.objects["2"].x;
this.objects["2"]["x"];
Essentially, in this case, just use the "pid" as they key for each object that contains the properties you want for each item.

Categories