Looping though unique keys - javascript

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>

Related

Iterating through JSON table in 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);

Find key contain dot and replace with #

I have nested object which can have any number of key at any depth.
I want to replace "." in all keys(if contain) with "#" How we can do this in efficient way.
Example Node js object
obj:{
"BotBuilder.Data.SessionState": {
"lastAccess": 1492886892545,
"version": 14,
"callstack": [
{
"id": "*:/",
"state": {
"BotBuilder.Data.WaterfallStep": 0,
"BotBuilder.Data.Intent": "welcomeDialog"
}
}
]
}
Currently i am using hard coded solution , but any keys can be possible in object at any level which contain "." I want generalize way to solve this problem
My code :
replaceDot:function(doc){
var finalobj={}
var finaldata={}
var finalcallstack=new Array();
console.log("doc==>",doc)
var callstack=doc["data"]["BotBuilder.Data.SessionState"]["callstack"]
for(var i = 0; i < callstack.length; i++) {
var tempcallstack={}
if("BotBuilder.Data.WaterfallStep" in callstack[i]["state"]){
tempcallstack["id"]=callstack[i]["id"]
var tempstate={}
tempstate["state"]=callstack[i]["state"]
tempstate["state"]["BotBuilder#Data#WaterfallStep"]=tempstate["state"]["BotBuilder.Data.WaterfallStep"]
tempstate["state"]["BotBuilder#Data#Intent"]=tempstate["state"]["BotBuilder.Data.Intent"]
delete tempstate["state"]["BotBuilder.Data.WaterfallStep"]
delete tempstate["state"]["BotBuilder.Data.Intent"]
tempcallstack["state"]=tempstate["state"];
finalcallstack.push(tempcallstack);
}
else{
finalcallstack.push(callstack[i]);
}
}
var obj={}
finalobj["lastAccess"]=doc["data"]["BotBuilder.Data.SessionState"]["lastAccess"]
finalobj["version"]=doc["data"]["BotBuilder.Data.SessionState"]["version"]
finalobj["callstack"]=finalcallstack;
obj["BotBuilder#Data#SessionState"]=finalobj
var secondrootobj={"BotBuilder#Data#SessionState":finalobj}
return secondrootobj;
}
Here's a function that takes an object or array, and target and replacement values for the keys of that object. It will then return a new object where instances of target are replaced with replacement (using String.prototype.replace) in the resulting object's keys.
var substituteKeyDeep = function(obj, target, replacement) {
// Get the type of the object. Array for arrays, Object for objects, null for anything else.
try {
var type = obj.constructor === Array ? Array
: (obj.constructor === Object ? Object : null);
} catch (err) {
// A try/catch is actually necessary here. This is because trying to access the `constructor` property
// of some values throws an error. For example `null.constructor` throws a TypeError.
var type = null;
}
if (type === Array) {
// Simply do a recursive call on all values in array
var ret = [];
for (var i = 0, len = obj.length; i < len; i++) {
ret[i] = substituteKeyDeep(obj[i], target, replacement);
}
} else if (type === Object) {
// Do a recursive call on all values in object, AND substitute key values using `String.prototype.replace`
var ret = {};
for (var k in obj) {
ret[k.replace(target, replacement)] = substituteKeyDeep(obj[k], target, replacement);
}
} else {
// For values that aren't objects or arrays, simply return the value
var ret = obj;
}
return ret;
};
var data = {
"BotBuilder.Data.SessionState": {
"lastAccess": 1492886892545,
"version": 14,
"callstack": [
{
"id": "*:/",
"state": {
"BotBuilder.Data.WaterfallStep": 0,
"BotBuilder.Data.Intent": "welcomeDialog"
}
}
]
}
};
var dataWithRenamedKeys = substituteKeyDeep(data, /\./g, '#');
console.log(dataWithRenamedKeys);
Note that in the example, the replacement value (/\./g) is a regex expression, not a string. This is because a regex expression with the global modifier (g) is required to replace ALL instances of the occurrence, not just the first, in the object's keys.
EDIT: Quick disclaimer! This solution will exceed the stack if substituteKeyDeep is called with an object that has circular references.

Most efficient way to remove a list of values from a javascript object by keyname

I need to find the most efficient way to remove values from a arbitrarily nested javascript object based on a list of 'keys-to-remove'. i.e.
var obj = {a:1, b:2, c:{d:1, e:1}};
var ignoreList = ["a","e"] (could also be ['a', 'c.e'])
removeIgnoredValues(obj, ignoreList) => {b:2, c:{d:1}}.
Now obviously this is easy enough to do if you don't care about efficiency, and my current implementation has been serving me well up till now. But now I'm having to deal with objects that have 6 levels and large arrays of data.
If anyone has a solution or link to one that would be awesome :)
Cheers
EDIT: Current implementation looks like this. It works (and deals with circular references). But is too slow.
/**
* Returns a sanitised string of an object, removing any functions and unwanted properties.
* #param {int} obj. The object to be stringified
* #param {Array[]} ignoreList. A array of object properties that should be removed.
*/
function sanitise(obj, ignoreList){
if(obj == undefined){
throw "Can't sanitise an undefined object"
}
var entry = JSON.parse(JSON.stringifyOnce(obj));
for(var i in entry){
if(entry.hasOwnProperty(i)){
if(contains(ignoreList, i)){
delete entry[i];
} else if(typeof(entry[i]) == "object" && entry[i] != null){
entry[i] = sanitise(entry[i], ignoreList);
}
}
}
return entry;
}
JSON.stringifyOnce = function(obj, replacer, indent){
var printedObjects = [];
var printedObjectKeys = [];
function printOnceReplacer(key, value){
var printedObjIndex = false;
printedObjects.forEach(function(obj, index){
if(obj===value){
printedObjIndex = index;
}
});
if ( key == ''){ //root element
printedObjects.push(obj);
printedObjectKeys.push("root");
return value;
}
else if(printedObjIndex+"" != "false" && typeof(value)=="object"){
if ( printedObjectKeys[printedObjIndex] == "root"){
return "(pointer to root)";
}else{
return "(see " + ((!!value && !!value.constructor) ? value.constructor.name.toLowerCase() : typeof(value)) + " with key " + printedObjectKeys[printedObjIndex] + ")";
}
}else{
var qualifiedKey = key || "(empty key)";
printedObjects.push(value);
printedObjectKeys.push(qualifiedKey);
if(replacer){
return replacer(key, value);
}else{
return value;
}
}
}
return JSON.stringify(obj, printOnceReplacer, indent);
};
Okay, figured out a pretty nice way. You just make an ignore list with roughly the same object structure as the object to be ignored.
function ignore(obj, list){
for(var i in list){
var type = Object.prototype.toString.call(list[i]);
if(type == "[object String]"){
delete obj[i];
}
else if (type == "[object Object]"){
ignore(obj[i], list[i])
}
else if (type == "[object Array]"){
var objList = obj[i];
var subList = list[i][0];
for(var n in objList){
ignore(objList[n], subList)
}
}
}
}
x = {a:1, b:[{c:1, d:1}, {c:1, d:1}, {c:1, d:1}], e:1}
ignoreList = {'e':'e', 'b':[{'c':'c'}]}
ignore(x, ignoreList) => {a:1, b:[{d:1}, {d:1}, {d:1}]}
We can create a look-up table object for the original object, to delete any given key in O(1) time. The implementation will involve you adding custom functions to add/remove from the object.
(function() {
var lookUpTable = {};
myObj.prototype.insert = function(key, value) {
// add key to the myObj
// insert an Entry for parent of key in lookUpTable
// lookUpTable = { "a" : [myObj.b, myObj, myObj.c.d.e] }
}
myObj.prototype.ignore = function(ignoreList) {
for( key in ignoreList ) {
for( parent in lookUpTable[key] )
delete parent[key];
delete lookUpTable [key];
}
}
}());
Now you can call the insert function to insert the key-value:
myObj.insert('a.b.c.d', ['p', 'g']);
and call the ignore function to delete the object:
myObj.ignore(['d', 'e']);
Sorry for just giving the incomplete code. But, you should be able to implement the details quiet easily. Hope you get the idea.
For the example given by you:
obj = {a:[{b:1, c:1}, {b:1, c:1}, {b:1, c:1}]
and you want to ignore all the 'b's. Note that the lookup table entry values were arrays and not just a single value. This is where the power of ignoring multiple entries with the same name, comes in. In this case, the entry for 'b' would be something like this.
lookupTable = {
b : [ // The parent objects of 'b'
obj['a'][0],
obj['a'][1],
obj['a'][2]
]
}
Basically, the lookuptable holds an array of references to all the objects that contain the key 'b'. So, you iterate through each of these parent objects and delete their 'b' entry.
$.each(lookupTable['b'], function( parent ) {
delete parent['b']; // Deletes 'b' inside of every parent object
});
You populate this lookup table entry while inserting into obj or while obj is loaded for the first time. If obj is hard-coded, you could also generate the lookupTable once and hard-code it. Probably in along with your minify Javascript scripts. Although populating it at run-time is also quiet fine.

How can I find the string value of a property given it's value

I created something similar to an ENUM like this:
var ContentStatusId = {
All: 0,
Production: 1,
Review: 2,
Draft: 3,
Concept: 4
}
so when I set:
var a = ContentStatusId.All
it gets the value of 0
How can I go in the other direction? If I know a = 0 and a comes from ContentStatusId then how can I get the string "All"?
Iterate over the properties until you find the one with the value you want
function findVal(obj, val) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) { // skip inherited properties
if (obj[prop] == val) {
return prop;
}
}
}
return false;
}
You'll have to iterate through the properties and check whether they equal the value on each iteration:
for (var property in ContentStatusId) {
if (ContentStatusId.hasOwnProperty(property)) {
if (ContentStatusId[property] == /*value to look for*/) {
console.log(property);
}
}
}
Demo
You can't do this directly. You'd have to store the key somewhere...
var a = ['All', ContentStatusId.All];
or, more advanced:
var a = {
source: ContentStatusId,
key: 'All',
update: function () {
this.value = this.source[this.key];
}
};
a.update();
Depending on what you have to do, you can either iterate over the object or if you have to do this more often, just create an array where you can do your reverse-lookup:
var ContentStatusIdReverse = ["All","Production",...]
ContentStatusIdReverse[0] // Yields "All"
You can create this array in the first place by iterating over your object once, and all consecutive lookups can be done via the array.

How do I write a recursive function in Javascript to add up all of the string values of a deeply nested object?

Say I have this object:
{
"prop1":"Hello",
"prop2":{
"prop1":{
"prop1":"Tablecloth",
"prop2":"Indians"
},
"prop2":"JuicyJuice"
},
"prop3":"Sponge",
"prop4":{"Bob":"Squarepants"}
}
I would like a recursive function that will return HelloTableclothIndiansJuicyJuiceSpongeSquarepants.
Whatever object I put it, I want it to cycle though until it gets all of the strings and adds them all up.
Thank you!
Here's a very simple implementation that should work for simple objects like this:
var walkProps = function(obj) {
var s = "";
for(var x in obj)
{
if(typeof obj[x] === "string")
s += obj[x];
else
s += walkProps(obj[x]);
}
return s;
}
Demonstration
Note, though, that that depends on the order in which for-in visits the properties on the object, which is not specified and can vary by engine and by how the object is constructed (for instance, the order in which the properties were added).
Update: With some slight modification, this can be used to return the values based on the alphabetical order of the keys. This method is not sensitive to implementation-dependent ordering of properties:
var walkProps = function(obj) {
var s = "", i = 0, keys = Object.keys(obj).sort(), i;
for(; i < keys.length; i++)
{
if(typeof obj[keys[i]] === "string")
s += obj[keys[i]];
else
s += walkProps(obj[keys[i]]);
}
return s;
}
So even if "prop3" comes before "prop2" it will still return the same output.
Demonstration
You would need to write a function that loops over an object's properties and see if they are a string, and then append the strings to an output. If the property is an object rather than a string, you would want to call the function on this object and append it's return value to your total output.
You can loop over an object's properties using for...in like:
var MyObject = {
'a': 'string1',
'b': 'string2'
};
for (var key in MyObject) {
var value = MyObject[key];
}
To check if a property is a string you would want to do:
typeof value === "string"
Which will return true/false accordingly.
As mentioned, for( var b in a ) may not preserve ordering:
// Return array of string values
function getStrings(a) {
if( typeof(a) == "string" ) return [a];
var list = [];
for( var b in a ) list = list.concat(getStrings(a[b]));
return list;
}
Applied to OP's data:
var a = {
"prop1":"Hello",
"prop2":{
"prop1":{
"prop1":"Tablecloth",
"prop2":"Indians"
},
"prop2":"JuicyJuice"
},
"prop3":"Sponge",
"prop4":{"Bob":"Squarepants"}
}
getStrings(a).join(); // "Hello,Tablecloth,Indians,JuicyJuice,Sponge,Squarepants"
// Or as asked for by OP (again, order is not guaranteed)
getStrings(a).join(''); // "HelloTableclothIndiansJuicyJuiceSpongeSquarepants"

Categories

Resources