Iterating through JSON table in Javascript - javascript

I'm trying to 'import' an external JSON table into my Javascript so I can iterate through it. Current results are that I just get [Object object] thing and no matter what I can't reach to the desired value.
My setup:
index.html
/js-
core.js
languages.js // <- same as below, but in js
languages.json
index.html:
<script src="js/languages.js"></script>
<script language="javascript" src="js/core.js"></script>
languages.js:
var data = {
"languages" : [{
"english" : {
"dontRefresh" : "Do not refresh the page!",
"..." : "...",
},
"russian" : {
...
},
}]
}
I tried the following and my local server crashed because it was just outputting "D" infinitely.
var langs = data;
function walk(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
console.log(val);
walk(val);
}
}
}
walk(langs);
Latest thing that worked was this:
var langs = data;
console.log(data.languages); // >[Object object] ("english:{}, russian:{}" I suppose)
console.log(data.languages.english); // >[Object object] ("dontRefresh":"...",} I suppose)
// BUT
console.log(data.languages.english['dontRefresh']); // > Undefined
What I'm trying to achieve is get the value of dontRefresh key. Simple enough, doesn't work.

At its core, your problem is simply that you're assuming strings aren't iterable.
Take this for example,
let string = 'abc';
for (key in string)
console.log(key);
// 0
// 1
// 2
// formatUnicorn
// truncate
// splitOnLast
// contains
Realize your problem yet? No?
Well, let's take the first iteration, where key is '0'. Does string.hasOwnProperty('0') return true? Yes.
This means we're going to recursively call walk on string[0]. What is string[0]? 'a', Another STRING!
Essentially, you'll infinitely iterate over the first character of the string when you pass a string to your walk algorithm. Luckily, it's easy to check for strings in JS using the typeof operator (mdn): typeof 'string' === 'string':
var data = {
"languages" : [{
"english" : {
"dontRefresh" : "Do not refresh the page!",
"funFact" : "kittens are nutritious!!!",
},
"russian" : {
},
}]
}
function walk(obj) {
if (typeof obj === 'string')
console.log(obj);
else
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
walk(val);
}
}
}
walk(data);

Related

Javascript inline replace undefined with empty string

I have this function:
function callWS(input) {
var output = {
"type" : input["type"] || "",
"mark" : input["mark"] || "",
"model" : input["model"] || "",
"year" : input["year"] || ""
};
return output;
}
I want the user to call this function in many ways:
callWS(); ==> {"type":"","mark":"","model":"","year":""}
callWS({"type":"moto"}); ==> {"type":"moto","mark":"","model":"","year":""}
callWS({"type":"moto","mark":"audi"}); ==> {"type":"moto","mark":"audi","model":"","year":""}
And in case a parameters is undefined, to initialize it as an empty string. Currently my function does not work in the first case, but in the other is working.
When I call it like callWS() I get:
Uncaught TypeError: Cannot read property 'type' of undefined
To be honest I don't know why it works for the 2 and 3 case but I need to make it work for the first case also. I know that if I use:
if (input["type"])
will do the trick but I need an inline solution. Is this possible somehow?
You have to supply input variable itself with default value too.
function callWS(input) {
input = input || {};
...
}
Otherwise you access properties on unexisting (undefined) object which lead to error (what you have now).
On other hand accessing unexisting properties on existing object isn't treated as error in JS.
You can write your own extend method and use that.
That way you can have a default object, giving it which ever default values and then merge it with the object passed into the function.
function extend(a, b){
for(var key in b){
if(b.hasOwnProperty(key)){
a[key] = b[key];
}
}
return a;
}
function callWS(input) {
var defaultInput = {
"type": "",
"mark": "",
"model":"",
"year": ""
}
var output = extend(defaultInput, input);
return output;
}
console.log(callWS());
console.log(callWS({"type":"moto"}));
console.log(callWS({"type":"moto","mark":"audi"}));
In ES6, write this as
function callWS({type = "", mark = "", model = "", year = ""} = {}) {
return {type, mark, model, year};
}
Here's another approach not involving ES6. Many libraries contain a default utility, which will apply some properties to another object if they are missing.
function deflt(obj, defaults) {
var keys = Object.keys(defaults);
for (var i = 0; i < keys.length: i++) {
var key = keys[i];
if (!(key in obj)) obj[key] = defaults[key];
}
return obj;
}
You can use this as follows:
function callWS(input) {
input = input || {};
deflt(input, {type: "", mark: "", model: "", year: ""});
return input;
}
Note: as written this will return a modified version of the input. Adjust as necessary if this is not what you want.

Looping though unique keys

I have a json object which I would like to loop through; however each object has a nested object which I can't access through dot notation due to the values being unique.
.__proto__
will give me consistent results; however I'd like to pull out the values starting with the "-Jg". Is it possible to do this through a regular expression or another method?
Edit:
I'm looping through the 'javascript object' with angular
var lognew = [];
angular.forEach(log, function(value, key) {
if(value){
if(value.substr(0,3) !== "-Jg" ){
this.push(value);
}
}
}, lognew);
console.log(lognew);
This currently returns:
TypeError: undefined is not a function
Just enumerate using for in and look at the first 3 characters of the object's key
for(var key in jsonObj){
if( key.substr(0,3) !== "-Jg" ) continue;
var nestedObject = jsonObj[key];
}
angular edit
var log = { "1": { "-Jga": "b" }, "2": { "-Jgc": "d" } };
var lognew = [];
angular.forEach(log, function(value, key) {
if(key){
if(key.substr(0,3) !== "-Jg" ){
this.push(value);
}
}
}, lognew);
console.log(lognew);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

jQuery - Iterate over Objects with numeric keys

I have an Object:
var myObj = { 5: "foo", 25: "bar" };
Now i want to walk through the object like this:
$.each(myObj, function(key, value) {
console.log(value);
});
The problem is, that jQuery walks 26 times through the array while value will be "undefined" in 24 cases (i debugged it - thats fact). So it walks through the object 5 times before it reaches the fifth value (which is the first one).
I dont want to convert the object to an array since i want to store the data back into the stack after handling the data.
Is this a jQuery-Bug, a bug of mine or how to solve this?
Additional Info:
Here's a screenshot of my Chrome's DevTools shows the object at runtime.
sT.data.tasks contains the same content as splitTicket - just needed to show splitTicket because sT.data.tasks is lost inside the $.each-Scope. (Maybe that indicates the problem?) - Another thing to note is the "length" of the object with key "2517" - Is that a Google-Chrome Bug, a javascript Bug or just correct?
-> Note that at the current breakpoint tkey is 0 (int) and tval is undefined (undefined) and since this code wont produce any Errors, it walks through it 2517 times (did not count^^)
It's not a bug at all. It looks like jQuery is just treating this object as an array and iterating over it in the normal way, starting from 0 and going to the largest key.
A for..in loop loops over the properties in the object.
Using the following only goes through 2 times:
for(key in myObj) {
console.log(myObj[key]);
};
Your example looks just fine to me:
var myObj = { 5: "foo", 25: "bar" };
$.each(myObj, function(key, value) {
console.log(value);
});
foo
bar
Looking at the jQuery source for $.each it appears to use for..in if your object isn't "arraylike".
I'd hypothesise that isArraylike returns true for your 'real' code (it's false for myObj)
jQuery $.each:
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) { break; }
}
jQuery isArraylike:
function isArraylike(obj) {
var length = obj.length,
type = jQuery.type(obj);
if (jQuery.isWindow(obj)) {
return false;
}
if (obj.nodeType === 1 && length) {
return true;
}
return type === "array" || type !== "function" && (length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj);
}
try this,
var myObj = { 5: "foo", 25: "bar" };
var i=0;
Object.keys( myObj ).forEach(function ( name, index ) {
var value = myObj[name];
console.log(++i);
console.log(name); // the property name
console.log(value); // the value of that property
console.log(index); // the counter
});
live DEMO
your code iterate two time at my side.but also you can try using this example.

how to filter object in javascript

I an working in javascript and stuck in understanding the objects.
Here is my scenario.
I have an object which in turn has multiple objects in it like.
data {
"aa" : object
"bb" : object
"cc" : object
}
//expanding aa bb and cc
aa {
name : "a"
type : "static"
value : "123"
}
bb {
name : "b"
type : "dyn"
value : "343"
}
cc {
name : "c"
type : "dyn"
value : "545"
}
Now what I want to achieve is that i have an object which should have those objects that have type = "dyn"
so i want to have a reqdata object like this
reqdata {
"bb" : object
"cc" : object
}
I have written a code to do this but it is not working as my reqdata has all the data.
var reqData = $.each (data, function(key, d){
if (type === "dyn")
return d;
});
Can any one guide me what the proper and efficient way of looping through the object.
Thanks any help and guidance will be appreciated
You need to create a new object, test the type property, and assign the current sub-object to the new one if the type is what you want.
// v--- Holds the results
var newObj = {};
// v--- The current sub-object
$.each(data, function(key, obj){
if (obj.type === "dyn") // <-- Test its `type` property
newObj[key] = obj; // <-- and assign to `newObj` if it matches
});
You should note that you're not making a copy of obj when you assign it to newObj. You're making a copy of a reference to obj.
This means that data and newObj share the same objects. Changes made via data are observable from newObj, and vice versa.
If you're used to functional programming, you can write your own filter function for objects:
function oFilter (obj, f) {
var result = {};
for (var x in obj) {
if (
obj.hasOwnProperty(x) &&
f(x,obj[x])
) {
result[x] = obj[x];
}
}
return result;
}
Then it'd be as you expected:
var reqData = oFilter(data, function(key,d){
if (d.type === "dyn") return true;
return false;
});
Similarly for map:
function oMap (obj, f) {
var result = {};
for (var x in obj) {
if (obj.hasOwnProperty(x)) {
result[x] = f(x,obj[x]);
}
}
return result;
}
Reduce doesn't make sense for objects though.
Shorter.
$(data).each( function(){
if(this.type === 'dyn'){ doStuff(this); }
} );
Now, IMO, constructor name is closer to type in JS. I'd build those objects with a function constructor names 'Dyn' and check <instance>.constructor.name for 'Dyn' but you would have to normalize for IE<=8 which involves parsing <instance>constructor.toString() so perhaps more trouble than it's worth.
But if you want to understand JS objects. Ditch the jQuery until you do. Learn how to use:
for(var x in object){
console.log('propertyLabel:' +x+', property:' + object[x]+'\n');
}
Then go back to understanding how jQuery itself works. Lots of juicy stuff under the hood there.

Is there any way to rename js object keys using underscore.js

I need to convert a js object to another object for passing onto a server post where the names of the keys differ for example
var a = {
name : "Foo",
amount: 55,
reported : false,
...
<snip/>
...
date : "10/01/2001"
}
needs to turn into
a = {
id : "Foo",
total : 55,
updated: false,
...
<snip/>
...
issued : "10/01/2001"
}
where I have lookup obj available for mapping all the keys
var serverKeyMap = {
name : "id",
amount : "total",
reported : "updated",
...
date : "issue"
}
Is there a function available in underscore.js or jQuery that I can use that does this functionality?
thanks
I know you didn't mention lodash and the answers already solve the problem, but someone else might take advantage of an alternative.
As #CookieMonster mentioned in the comments, you can do this with _.mapKeys:
_.mapKeys(a, function(value, key) {
return serverKeyMap[key];
});
And the fiddle: http://jsfiddle.net/cwkwtgr3/
Similar to #pimvdb, you can also do it with a _.reduce:
_.reduce(a, function(result, value, key) {
key = map[key] || key;
result[key] = value;
return result;
}, {});
Fiddle: http://jsfiddle.net/T9Lnr/39/
As far as I know there is no function built into either of these two libraries. You can make your own fairly easily, though: http://jsfiddle.net/T9Lnr/1/.
var b = {};
_.each(a, function(value, key) {
key = map[key] || key;
b[key] = value;
});
You could copy the values to the new properties with standard JavaScript, and remove the original properties with omit, as follows:
a.id = a.name;
a.total = a.amount;
a.updated = a.reported;
a = _.omit(a, 'name', 'amount', 'reported');
// key_map: {old_name1: new_name1, ... }
function rename_keys(object, key_map, is_picked=false){
keys = _.keys(key_map);
new_keys = _.values(key_map);
picked = _.pick(object, keys);
renamed = _.object(new_keys, _.values(picked));
if(is_picked) return renamed;
return _.chain(object).omit(keys).extend(renamed).value();
}
This may be slower than above answers.
No there is no function in either library that explicitly renames keys. Your method is also the fastest (see jsperf tests.) Your best bet, if possible, is to refactor either the client side or server side code so the objects are the same.
I have a transformation operator and would just like to apply it to all keys. I forked pimvdb's fiddle to produce a simple example. In this case it Capitalizes the key. And it dynamically builds the keymap, which I needed to assure works (thanks JSFiddle).
Here is the changed code:
var keymap = {};
_.each(a, function(value, key) {
var oldkey = key;
key = capitalize(key);
keymap[oldkey] = key;
});
_.each(a, function(value, key) {
key = keymap[key] || key;
b[key] = value;
});
Fiddle:
http://jsfiddle.net/mr23/VdNjf/
It's been solved here https://stackoverflow.com/a/30940370/1360897
var keyMapping = {'PropertyA': 'propertyA', ..., 'PropertyF': 'propertyNEW'}
and also a mapping of old and new values, like this
var valueMapping = {'Y': true, 'F': false}
And then using _.map and _.transform, you can transform the object, like this
var result = _.map(allItems, function(currentObject) {
return _.transform(currentObject, function(result, value, key) {
if (key === 'PropertyF' || key === 'PropertyG') {
value = valueMapping(value);
}
result[keyMapping[key]] = value;
});
});
Why don't you use this simple java script ? Value of any key:value pair should be string/number/Boolean.
<script type="text/javascript">
var serverKeyMap = {
name : "id",
amount : "total",
reported : "updated"
};
var a = {
name : "Foo",
amount: 55,
reported : false
};
var b={}; // b is object where you will get your output
for(i in serverKeyMap) b[serverKeyMap[i]]=a[i];
console.log(b); // It gives what you need.
</script>
As user2387823 was saying above 👆 using omit is a great option. For example you could write something like this
function updateObjKey(obj, currentKey, newKey) {
var keyValue = obj[currentKey];
obj = _.omit(obj, [currentKey]);
obj[newKey] = keyValue;
return obj;
}
this ES2015/2017 version 🧙‍♂️
function objectMap(source,keyMap) {
return Object.entries(keyMap).reduce((o,[key , newKey]) => {
o[newKey]=source[key]
return o;},{})
}
const obj = {
name : "Foo",
amount: 55,
reported : false,
date : "10/01/2001"
}
const serverKeyMap = {
name : "id",
amount : "total",
reported : "updated",
date : "issue"
}
const result = objectMap(obj,serverKeyMap);
console.log('🎬 =>' , result);
[Object.entries][1] is es2017 feture will return object key and
value as array
[["name", "id"],["amount", "total"],...]
You really don't need underscore/lodash for this ... nowadays anyways (I realize the question was asked 9 years ago, but this question is (still) ranked highly in search results and I came across it today :-) )
Here's another plain ES2015/2017 version that I like, inspired by #malbarmavi's answer (there's probably a bunch of other plain JS functions out there, but I didn't come across any others in my brief search):
// A general key transform method. Pass it a function that accepts the old key and returns
// the new key.
//
// #example
// obj = transformKeys(obj, (key) => (
// key.replace(/\b(big)\b/g, 'little')
// ))
export function transformKeys(source, f) {
return Object.entries(source).reduce((o, [key, value]) => {
o[f(key) || key] = value
return o
}, {})
}
// Provide an object that maps from old key to new key
export function rekeyObject(source, keyMap) {
transformKeys(source, key => keyMap[key])
}
I referred the lodash documentation ans found mapKeys
https://lodash.com/docs/4.17.15#mapKeys
_.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) {
return key + value;
});
// => { 'a1': 1, 'b2': 2 }
this perfectly renames the keys and return an object containing the modified desirable object
Using underscore omit and spread operator.
a = _.omit({
...a,
id: a.name,
total: a.amount,
updated: a.reported,
}, ['name', 'amount', 'reported']);
Key assignments below spread operator loads new keys and omit omits the old ones.
You can create your new custom function :
lodash.rename = function(obj, keys, newKeys) {
keys.map((key, index) => {
if(lodash.includes(lodash.keys(obj), key)) {
obj[newKeys[index]] = lodash.clone(obj[key], true);
delete obj[key];
}
});
return obj;
};
Or else if you want to edit only one keyName:
lodash.rename = function(obj, key, newKey) {
if(lodash.includes(lodash.keys(obj), key)) {
obj[newKeys[index]] = lodash.clone(obj[key], true);
delete obj[key];
}
return obj;
};
Using lodash
var obj = _.renameKeys( { 1 : "Geeks",
2 : "Computer_Science_Portal" },
{ 1 : "g", 2 : "c" });
so in your case, you want to apply the serverKeyMap onto object a :
var obj = _.renameKeys(a, serverKeyMap);
from https://www.geeksforgeeks.org/lodash-_-renamekeys-method/

Categories

Resources