I'm looking for an easy and efficient way to pigeon-hole business objects to be recalled later by ID in some sort of dictionary. I have this method working but it appears it may be unnecessarily using a lot of memory.
var objects = [{ ID: 20, Description: 'Item 1'},
{ ID: 40, Description: 'Item 2'},
{ ID: 60, Description: 'Item 3'}];
var objectsByID = [];
$.each(objects, function (index, o) {
objectsByID[o.ID] = o;
});
var itemID40 = objectsByID[40];
Firebug tells me that objectsByID has undefined array elements in-between the ID numbers that have been added, like so:
[undefined, ... ,
Object { ID=20, Description="Item 1"}, ... ,
Object { ID=40, Description="Item 2"}, ... ,
Object { ID=60, Description="Item 3"}]
Are these array indexes actually assigned and using memory, or is this a conceptual view?
Also, should I be doing this?
JavaScript arrays are sparse, so no, you're aren't taking up extra memory like that. The downside though, and the reason why consoles display it like that, is that the length property will be equal to your highest index + 1.
Some explanation:
All array indices are converted into strings and treated exactly the same as object properties. You can do the following test in the console:
var a = [];
a[100] = "hello";
a["100"]; // "hello"
a.hasOwnProperty("100"); // true
a.hasOwnProperty("0"); // false
To show that this isn't the same as a property that's declared but 'undefined':
a[0] = undefined;
a.hasOwnProperty("0"); // true
var objects = {
"20": { ID: 20, Description: "Item 1" }
"40": { ID: 40, Description: "Item 2" }
}
Don't use an array. Use an object and duplicate the key. You can still treat it as an array but with an array it will create spaces 1-19 here you just have two keys which happen to be called "20" and "40".
Of course you can just use arrays anyway because it doesn't really matter that much in terms of memory usage with a bunch of undefined objects. We don't allocate blocks of memory equivelant to the largest block in the array like we do in C.
Related
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.
How can I access Name's value and assign it to an variable?
var arr = [
{Name: "Jason",
Title: "Student",
Image: "asdf",
Status: "Happy"}
];
Try this:
var [{Name: name}] = arr;
This uses ES6 destructuring assignment.
First, the outermost [] is a way of referring to an array on the right hand side (in this example, arr). Things placed within these square brackets (here's there's only one) refer to the first, second, and succeeding values of that array. So here, the {Name: name} portion refers to the first (0th) element of the array. In other words, it is equivalent to
var {Name: name} = arr[0];
The inner {} is a way of referring to objects and picking them apart. {Name: name} says to find the Name property of the object being picked apart. Then the : name part says to rename it to name. Since all this is occurring in the context of a var statement, the result is declare a new variable with the name name and assign the value being picked out to it.
Here's the more detailed sequence:
var // Start a variable declaration
[ // Pick apart an array on the RHS, starting with 1st element
{ // Pick apart an object
Name // Find the property named `Name` in that object
:name // Rename it; this is the variable that will be declared!
} // Done picking apart the object
] // Done picking apart the array
= arr; // Specify the thing to deconstruct
Access the element at index 0 of array using bracket notation, then access property name Name of object using dot or bracket notation
var arr = [
{Name: "Jason",
Title: "Student",
Image: "asdf",
Status: "Happy"}
];
var name = arr[0].Name; // alternatively, `arr[0]["Name"]`
var arr = [
{Name: "Jason",
Title: "Student",
Image: "asdf",
Status: "Happy"}
];
var myname = arr[0]['Name'];
console.log(myname);
I have two arrays. The first one is just a list of some numbers, say, magicNumbers = [1,2,3,15,33]. The second one is an array of objects, all having a property magic, like that: magicObjects = [ { 'magic': 1 }, {'magic: 2}, {'magic': 15} ]
I need to create a new array, containing objects from magicObject, in the same order as value of magic property is in the magicNumbers array, and those places from magicNumbers that that do not have a corresponding object in magicObjects should be filled with null. In our example, this should give:
[ { 'magic': 1 }, {'magic: 2}, null, {'magic': 15}, null ]
It's quite easy to implement it in a straightforward manner with _.map() and _.find():
_.map(magicNumbers,
function(num) {
return _.find(magicObjects,
function(v) { return v.magic == num }
) || null;
});
Any ideas how do it properly in a javascript-way, with underscore.js, or maybe just more effective?
Usually this is done by populating a map id: object and fetching objects from the map as you go. So you get N+M performance instead of N*M:
console.info=function(x){document.write('<pre>'+JSON.stringify(x,0,3)+'</pre>')}
//--
magicNumbers = [1,2,3,15,33];
magicObjects = [ { 'magic': 1 }, {'magic': 2}, {'magic': 15} ];
var mapping = {};
magicObjects.forEach(o => mapping[o.magic] = o);
var result = magicNumbers.map(n => mapping[n] || null);
console.info(result);
I have two arrays of objects. arrayOne contain items type of myObject1:
var myObject1 = {
Id: 1, //key
params: { weight: 52, price: 100 },
name: "",
role: ""
};
arrayTwo contained items type of myObject2:
var myObject2 = {
Id: 1, //key
name: "real name",
role: "real role"
};
I want to copy all names and roles from arrayTwo to arrayOne.
id is the key, both arrays contains myObjects with that is mached by 'id`.
If the two arrays are guaranteed to be congruent, then with the use of jQuery.extend(), the code is trivial :
$.each(arrayOne, function(i, obj) {
$.extend(obj, arrayTwo[i]);
});
A solution that runs in linear time.
var arrayOne; // Array containing objects of type myObject1
var arrayTwo; // Array containing objects of type myObject2
var tempObj = {};
// Transform arrayOne to help achieve a better performing code
arrayOne.forEach(function(obj){
tempObj[obj.id] = obj;
});
// Runs on linear time O(arrayTwo.length)
arrayTwo.forEach(function(obj){
// Note, since I'm not adding any thing to the arrayTwo
// I can modify it in this scope
var match = tempObj[obj.id];
if(match){
// If a match is found
obj.name = match.name;
obj.role = match.role;
}
});
I was wondering how to convert an array to an object by splitting the values ?
var obj = {
id: '',
val: ''
}
By this I mean - if I have an array like
["abc12", "abc1", "def12", "abc454"]
How I could split the first 3 values off - so it end up like an object like:
{id: 'abc', val: 12}, {id: 'abc', val: 1} ...
source.map(function(x) {
return { id: x.substr(0,3), val: +(x.substr(3)) };
}
EDIT: Not an answer to the question
This will not result in the desired mapped array, but in a single object.
Sorry, misread your question :(
The answer below will return one single Object and not an array of Objects.
You can easily fold such arrays into objects with the array's reduce method:
var source = ["abc12", "abc1", "def12", "abc454"];
var obj = source.reduce(function(o, str) {
var key = str.substr(0, 3)
var value = parseInt(str.substr(3))
o[key] = value;
return o;
}, {})
// obj = { abc: 454, def: 12 }
One solution is to map your array (like herby noted) with a function that converts an element of your array to an object of your desired form.
In this case, the id is represented by all the characters before the first digit and the val represents the digits from the back of the string :
source.map(function(x) {
return {
id: x.replace(/(^.*?)\d+$/,'$1'),
val: parseInt(x.replace(/^.*?(\d+)$/,'$1'),10)
};
});
Here's a working demo
Ps: You may want to be careful and check if the map method exists in the Array prototype chain, because older browsers may not have this implemented. Here's a link that explains how to cover this browser incompatibility : http://www.tutorialspoint.com/javascript/array_map.htm.