I had already functioning code.
Some part of the code should change two values of queryParams array:
myParam1 = 'sort_by';
myParam2 = 'sort_order';
queryParams[myParam1] = 'title';
if (queryParams[myParam2] == 'ASC') {
queryParams[myParam2] = 'DESC';
} else { //DESC or undefined
queryParams[myParam2] = 'ASC';
}
queryPairs = [];
for (var index in queryParams) {
queryPairs.push(index + "=" + queryParams[index]);
}
Unfourtunately some logic has changed and now sometimes queryParams has length 0 at the beginning of this script and then this part failes.
queryParams[myParam1] = 'title';
and
queryParams[myParam2] = 'DESC'
lines do not change queryParams.length,so the length is still 0 and the loop
for (var index in queryParams){
do not work as expected.
I have not found how to add key/value into the array.
You're being a bit confused by the fact that JavaScript's standard arrays aren't really arrays at all.
You're not using queryParams as an array in the code you've quoted. You're using it as an object. Objects don't have a length property by default.
You haven't said what changed, so it's difficult to help you, but note that even if queryParams is an array and you add a property to it as you are, the length will remain 0. The length of an array only relates to a special class of properties (ones that meet the definition of an "array index"), not to all properties.
Some examples that may help:
var a = []; // a is an array
console.log(a.length); // "0"
a['foo'] = 'bar';
console.log(a.length); // "0", the `foo` property doesn't have any effect on `length`
a[0] = 'zero';
console.log(a.length); // "1", the `0` property *does* have an effect on `length`
var o = {}; // o is an object
var myParam1 = 'sort_by';
var myParam2 = 'sort_order';
var propertyName;
o[myParam1] = 'foo';
o[myParam2] = 'DESC';
for (propertyName in o) {
console.log(propertyName); // "sort_by" and then "sort_order" (the order is not defined)
}
Related
If accessing properties of undefined value, I'm getting an exception:
let object = {}
let n = object["foo"].length;
VM186:1 Uncaught TypeError: Cannot read property 'length' of undefined
at :1:12
I want to get a default value in this case instead of an exception, but the way I'm doing it now seems too verbose:
let n = 0;
if (object.hasOwnProperty("foo")) {
n = object["foo"].length;
}
Is there a more simple and elegant way to do this? Possibly, using ES6.
The method mentioned by #lleaon will work only when the value is undefined and won't work for other falsy values like null.
Here is a technique I use often to safely access nested objects in JavaScript. I picked it up a year ago from another SO answer.
const obj = {};
const arrLength = (obj.foo || []) || 0;
console.log(arrLength); // 0
You can check deep nest level like this,
const obj = {};
const arrLength = ((obj.nestedObj || {}).foo || []) || 0;
console.log(arrLength); // 0
In case you're iterested, I wrote a blog post on it a while back.
Safely Accessing Nested Objects in JavaScript
Not sure if more elegant, but object destructuring can assign default values.
It wont prevent you from null values though. Just undefined
const obj = {};
const { foo: { length = 0 } = [] } = obj;
console.log(length)
using es6 you can check whether any key is there for the object by checking using Object.keys(object) which will give an array of keys Object.keys, checking it with the length will give that object is empty or not and also checking one more condition whether the constructor of object is an Object.
Please see the below code. if those two conditions are satisfied which means object is empty and you can assign a default value
let object = {}
if(Object.keys(object).length === 0 && object.constructor === Object){
// assign a default value if the object is empty
object["foo"] = "bar"
}
console.log("object is empty default value will be assigned", object)
Try ternary operator:
let n =object["foo"] ? object["foo"].length : 0;
To not write twice object["foo"] you can also do something like that:
let n = object["foo"];
n = n ? n.length : 0;
We have two possible undefined values to check. First, the key needs to exist on dictionary. After that, the object should be an array or a struct with the property length.
One great way to go is to use || to define a default value when undefined. But it will not work if you tried to check length of an undefined value, without dealing with this first.
Example
let object = {};
let r = (object["foo"] || 333);
let l = r.length || 111;
let oneLiner = (object["foo"] || 333).length || 111;
console.log("Default Value: " + r);
console.log("Default Length: " + l);
console.log("OneLiner: " + oneLiner);
Another Example
A simpler example, closer of your use case;
let object = {};
let length = (object["foo"] || []).length;
console.log(length);
I'm learning JavaScript and I was looking for a while about this and I have not got any answer about this. My question is if there is any rule to define a JSON key in JavaScript.
For example, in python there is a rule defining dict and is All the keys must be of an immutable data type such as strings, numbers, or tuples.
var json = {};
json[""] = "White space";
json[" "] = "Two white space";
var emptyJSON = {};
var emptyArray = [];
function a (){}
json[a] = "Function";
json[emptyJSON] = "Json";
json[emptyArray]= "Array";
//I don't know why this property whit an empty array does not appear when I console the json
console.log("Print the entire object =>", json);
//But it appears when I console the specific property
console.log("Print empty array property =>", json[emptyArray]);
Object keys are strings. Anything that is not a string is converted to a string.
var obj = {};
obj[1] = 1;
console.log(obj["1"]);
obj[{}] = 2;
console.log(obj["[Object object]"]);
obj[[1, 2]] = 3;
console.log(obj["1,2"]);
I have a bunch of object attributes coming in as dot-delimited strings like "availability_meta.supplier.price", and I need to assign a corresponding value to record['availability_meta']['supplier']['price'] and so on.
Not everything is 3 levels deep: many are only 1 level deep and many are deeper than 3 levels.
Is there a good way to assign this programmatically in Javascript? For example, I need:
["foo.bar.baz", 1] // --> record.foo.bar.baz = 1
["qux.qaz", "abc"] // --> record.qux.qaz = "abc"
["foshizzle", 200] // --> record.foshizzle = 200
I imagine I could hack something together, but I don't have any good algorithm in mind so would appreciate suggestions. I'm using lodash if that's helpful, and open to other libraries that may make quick work of this.
EDIT this is on the backend and run infrequently, so not necessary to optimize for size, speed, etc. In fact code readability would be a plus here for future devs.
EDIT 2 This is NOT the same as the referenced duplicate. Namely, I need to be able to do this assignment multiple times for the same object, and the "duplicate" answer will simply overwrite sub-keys each time. Please reopen!
You mentioned lodash in your question, so I thought I should add their easy object set() and get() functions. Just do something like:
_.set(record, 'availability_meta.supplier.price', 99);
You can read more about it here: https://lodash.com/docs#set
These functions let you do more complex things too, like specify array indexes, etc :)
Something to get you started:
function assignProperty(obj, path, value) {
var props = path.split(".")
, i = 0
, prop;
for(; i < props.length - 1; i++) {
prop = props[i];
obj = obj[prop];
}
obj[props[i]] = value;
}
Assuming:
var arr = ["foo.bar.baz", 1];
You'd call it using:
assignProperty(record, arr[0], arr[1]);
Example: http://jsfiddle.net/x49g5w8L/
What about this?
function convertDotPathToNestedObject(path, value) {
const [last, ...paths] = path.split('.').reverse();
return paths.reduce((acc, el) => ({ [el]: acc }), { [last]: value });
}
convertDotPathToNestedObject('foo.bar.x', 'FooBar')
// { foo: { bar: { x: 'FooBar' } } }
Just do
record['foo.bar.baz'] = 99;
But how would this work? It's strictly for the adventurous with a V8 environment (Chrome or Node harmony), using Object.observe. We observe the the object and capture the addition of new properties. When the "property" foo.bar.baz is added (via an assignment), we detect that this is a dotted property, and transform it into an assignment to record['foo']['bar.baz'] (creating record['foo'] if it does not exist), which in turn is transformed into an assignment to record['foo']['bar']['baz']. It goes like this:
function enable_dot_assignments(changes) {
// Iterate over changes
changes.forEach(function(change) {
// Deconstruct change record.
var object = change.object;
var type = change.type;
var name = change.name;
// Handle only 'add' type changes
if (type !== 'add') return;
// Break the property into segments, and get first one.
var segments = name.split('.');
var first_segment = segments.shift();
// Skip non-dotted property.
if (!segments.length) return;
// If the property doesn't exist, create it as object.
if (!(first_segment in object)) object[first_segment] = {};
var subobject = object[first_segment];
// Ensure subobject also enables dot assignments.
Object.observe(subobject, enable_dot_assignments);
// Set value on subobject using remainder of dot path.
subobject[segments.join('.')] = object[name];
// Make subobject assignments synchronous.
Object.deliverChangeRecords(enable_dot_assignments);
// We don't need the 'a.b' property on the object.
delete object[name];
});
}
Now you can just do
Object.observe(record, enable_dot_assignments);
record['foo.bar.baz'] = 99;
Beware, however, that such assignments will be asynchronous, which may or may not work for you. To solve this, call Object.deliverChangeRecords immediately after the assignment. Or, although not as syntactically pleasing, you could write a helper function, also setting up the observer:
function dot_assignment(object, path, value) {
Object.observe(object, enable_dot_assignments);
object[path] = value;
Object.deliverChangeRecords(enable_dot_assignments);
}
dot_assignment(record, 'foo.bar.baz', 99);
Something like this example perhaps. It will extend a supplied object or create one if it no object is supplied. It is destructive in nature, if you supply keys that already exist in the object, but you can change that if that is not what you want. Uses ECMA5.
/*global console */
/*members split, pop, reduce, trim, forEach, log, stringify */
(function () {
'use strict';
function isObject(arg) {
return arg && typeof arg === 'object';
}
function convertExtend(arr, obj) {
if (!isObject(obj)) {
obj = {};
}
var str = arr[0],
last = obj,
props,
valProp;
if (typeof str === 'string') {
props = str.split('.');
valProp = props.pop();
props.reduce(function (nest, prop) {
prop = prop.trim();
last = nest[prop];
if (!isObject(last)) {
nest[prop] = last = {};
}
return last;
}, obj);
last[valProp] = arr[1];
}
return obj;
}
var x = ['fum'],
y = [
['foo.bar.baz', 1],
['foo.bar.fum', new Date()],
['qux.qaz', 'abc'],
['foshizzle', 200]
],
z = ['qux.qux', null],
record = convertExtend(x);
y.forEach(function (yi) {
convertExtend(yi, record);
});
convertExtend(z, record);
document.body.textContent = JSON.stringify(record, function (key, value, Undefined) {
/*jslint unparam:true */
/*jshint unused:false */
if (value === Undefined) {
value = String(value);
}
return value;
});
}());
it's an old question, but if anyone still looking for a solution can try this
function restructureObject(object){
let result = {};
for(let key in object){
const splittedKeys = key.split('.');
if(splittedKeys.length === 1){
result[key] = object[key];
}
else if(splittedKeys.length > 2){
result = {...result, ...{[splittedKeys.splice(0,1)]: {}} ,...restructureObject({[splittedKeys.join('.')]: object[key]})}
}else{
result[splittedKeys[0]] = {[splittedKeys[1]]: object[key]}
}
}
return result
}
Based on my understanding of the docs (here and here) one would need a reference to the memory address for it to work:
const foo = {};
const map = new Map();
map.set(foo,'123'); // Can only be done if memory address of `foo` is known. Any other shimming would require stringification of foo
This is because JavaScript object {} keys can only be strings (at least in ES5).
Yet I see Map shim being available : https://github.com/zloirock/core-js#map. I tried reading the source but its too neatly abstracted (internally uses strong collection which then imports 10 more files)
Question
Answer any of the following please
Is there a simple trick to it and can it truly even be done (without stringification)?
Perhaps it mutates foo to store some string on it and then uses that as the key?
Something else and maybe I am reading the docs wrong?
There are two ways that come to mind. First, obviously, you can have an array of keys, and search it linearly:
Map1 = {
keys: [],
values: [],
};
Map1.set = function(key, val) {
var k = this.keys.indexOf(key);
if(k < 0)
this.keys[k = this.keys.length] = key;
this.values[k] = val;
};
Map1.get = function(key) {
return this.values[this.keys.indexOf(key)];
};
foo = {};
bar = {};
Map1.set(foo, 'xxx');
Map1.set(bar, 'yyy');
document.write(Map1.get(foo) + Map1.get(bar) + "<br>")
The second option is to add a special "key" marker to an object which is used as a key:
Map2 = {
uid: 0,
values: {}
};
Map2.set = function(key, val) {
key = typeof key === 'object'
? (key.__uid = key.__uid || ++this.uid)
: String(key);
this.values[key] = val;
};
Map2.get = function(key) {
key = typeof key === 'object'
? key.__uid
: String(key);
return this.values[key];
};
foo = {};
bar = {};
Map2.set(foo, 'xxx');
Map2.set(bar, 'yyy');
document.write(Map2.get(foo) + Map2.get(bar) + "<br>")
Unlike the 1st option, the second one is O(1). It can be done more accurately by making uid non-writable/enumerable. Also, each Map should have its own "uid" name (this can be easily set up in the Map constructor).
The trick is to store in an array and perform the lookup in O(n) time by iterating and using strict comparison—instead of using a true hash function which would be O(1) lookup. For example consider this:
var myObj = {};
var someArray = [{}, {}, myObj, {}];
console.log(someArray.indexOf(myObj)); // returns 2
Here is my implementation from another answer: Javascript HashTable use Object key
function Map() {
var keys = [], values = [];
return {
put: function (key, value) {
var index = keys.indexOf(key);
if(index == -1) {
keys.push(key);
values.push(value);
}
else {
values[index] = value;
}
},
get: function (key) {
return values[keys.indexOf(key)];
}
};
}
Have a look at my polyfill here. I am not advertising my polyfill, rather all I am saying is that it is the simplest and most straightforward I have yet to find, and thus it is the most suitable for learning and educational analysis. Basically, how it works is it uses a lookup table for the keys and a corresponding value table as visualized below.
var k = {}, j = [], m = document, z = NaN;
var m = new Map([
[k, "foobar"], [j, -0xf], [m, true], [z, function(){}]
]);
Index Key Value
##### ################ ################
0. k ({}) "foobar"
1. j ([]) -15
2. m (Document) true
3. z (NaN) function(){}
Internally, each item is stored at a different index, or at least that is the way I like to do it. This is also similar to the way the browser implements it internally. Unfortunately, I have seen some other polyfills that attempt to instead store the key on the object itself, and mess with all the internal methods to hide it, resulting in the entire webpage running 10000% slower and the maps being so slow that it takes nearly a full millisecond just to set and get new properties. Plus, I cannot fathom how many countless hours they waisted just trying to monkey-patch all the internal methods such as hasOwnProperty.
As for how and why my polyfill works, javascript objects are stored at a different place in memory. That is why [] !== [] and indexOf on an array of javascript objects works properly. It is because they are not the same array.
I know this has been asked a lot of times, but how do I fix exactly this thing?
I have a map[][] array (contains tile ids for a game) and I need to copy it to pathmap[][] array (contains just 0's and 1's, it is a path map), however when I do so..
function updatepathmap(){
pathmap = [];
var upm_x = 0;
while (upm_x < map.length){
var upm_y = 0;
while (upm_y < map[upm_x].length){
pathmap[][]
if (canPassthrough(map[upm_x][upm_y])) {
pathmap[upm_x][upm_y] = 1;
} else {
console.log(upm_x);
console.log(upm_y);
pathmap[upm_x][upm_y] = 0;
}
upm_y++;
}
upm_x++;
}
console.log(map);
console.log(pathmap);
}
..it gives me Cannot set property '0' of undefined typeerror at line pathmap[upm_x][upm_y] = 0;
Despite the foo[0][0] syntactic sugar, multi-dimensional arrays do not really exist. You merely have arrays inside other arrays. One consequence is that you cannot build the array in the same expression:
> var foo = [];
undefined
> foo[0][0] = true;
TypeError: Cannot set property '0' of undefined
You need to create parent array first:
> var foo = [];
undefined
> foo[0] = [];
[]
> foo[0][0] = true;
true
You can determine whether it exists with the usual techniques, e.g.:
> var foo = [];
undefined
> typeof foo[0]==="undefined"
true
> foo[0] = true;
true
> typeof foo[0]==="undefined"
false
I would have thought pathmap[][] was a syntax error, I'm surprised you're not seeing one.
Before you can use an array at pathmap[upm_x], you must create an array at pathmap[upm_x]:
pathmap[upm_x] = [];
This would be the first line in your outer while, so:
while (upm_x < map.length){
pathmap[upm_x] = [];
// ...
Remember that JavaScript doesn't have 2D arrays. It has arrays of arrays. pathmap = [] creates the outer array, but doesn't do anything to create arrays inside it.
Side note:
var upm_x = 0;
while (upm_x < map.length){
// ...
upm_x++;
}
is an error-prone way to write:
for (var upm_x = 0; upm_x < map.length; upm_x++){
// ...
}
If you use while, and you have any reason to use continue or you have multiple if branches, it's really easy to forget to update your looping variable. Since looping on a control variable is what for is for, it's best to use the right construct for the job.
Side note 2:
Your code is falling prey to The Horror of Implicit Globals because you don't declare pathmap. Maybe you're doing that on purpose, but I wouldn't recommend it. Declare your variable, and if you need it outside your function, have your function return it.
Side note 3:
map would make this code a lot simpler:
function updatepathmap(){
var pathmap = map.map(function(outerEntry) {
return outerEntry.map(function(innerEntry) {
return canPassthrough(innerEntry) ? 1 : 0;
});
});
console.log(map);
console.log(pathmap);
}