how to not print multiple times? - javascript

I had a task to print out all the fields of a given object
and i solved it like this:
function print(jsonObject) {
if (typeof(jsonObject) === 'object') {
for (var prop in Object.keys(jsonObject)) {
if (typeof(jsonObject[prop]) === 'object') {
print(jsonObject[prop]);
}else{
console.log(prop + ':' + jsonObject[prop]);
}
}
}
}
But what do I do if i had an object refrenced to one another?
like so:
let one = {
name: 'name',
age: 21
}
let two = {
name: {
firstName: 'name',
lastName: 'lastname'
}
age: 22,
}
two.ref = one
one.ref = two;
What is the stop condition?
thanks

Simple naive reference lookup, that checks if the same object has been traversed using a WeakSet().
Will have false positives on property values that reference the same object.
eg: one.ref1=one.ref
printer(one)
function printer(jsonObject) {
const seen = new WeakSet();
print(jsonObject)
function print(jsonObject) {
if (typeof(jsonObject) === 'object') {
if(jsonObject) seen.add(jsonObject);
for (var prop of Object.keys(jsonObject)) {
if (typeof(jsonObject[prop]) === 'object') {
if(jsonObject[prop]!==null) {
if(seen.has(jsonObject[prop])) {
console.log(`circular ref at key ${prop}:`,jsonObject[prop]);
return;
}
else
print(jsonObject[prop]);
seen.add(jsonObject[prop]);
}
}else{
console.log(prop + ':' + jsonObject[prop]);
}
}
}
}
}
<script>
let one = {
name: 'name',
age: 21
}
let two = {
name: {
firstName: 'name',
lastName: 'lastname'
},
age: 22,
}
two.ref = one;
one.ref = two;
</script>

Related

Convert object to array of prorperties

I need to convert object:
{
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
}
to array:
[{
key: "name",
propertyValue: "Test Name",
},
{
key: "middleName",
propertyValue: null,
},
{
key: "university.isGraduated",
propertyValue: true,
},
{
key: "university.speciality",
propertyValue: "Computer Science",
},
{
key: "university.country.code",
propertyValue: "PL"
}];
I wrote algorithm, but it's pretty dummy, how can I improve it? Important, if object has nested object than I need to write nested object via dot (e.g university.contry: "value")
let arr = [];
Object.keys(parsedObj).map((key) => {
if (parsedObj[key] instanceof Object) {
Object.keys(parsedObj[key]).map((keyNested) => {
if (parsedObj[key][keyNested] instanceof Object) {
Object.keys(parsedObj[key][keyNested]).map((keyNestedNested) => {
arr.push({ 'key': key + '.' + keyNested + '.' + keyNestedNested, 'propertyValue': parsedObj[key][keyNested][keyNestedNested] })
})
} else {
arr.push({ 'key': key + '.' + keyNested, 'propertyValue': parsedObj[key][keyNested] })
}
})
} else {
arr.push({ 'key': key, 'propertyValue': parsedObj[key] })
}
});
Thanks
A recursive function implementation.
I have considered that your object consist of only string and object as the values. If you have more kind of data types as your values, you may have to develop on top of this function.
const myObj = {
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
}
const myArr = [];
function convertObjectToArray(obj, keyPrepender) {
Object.entries(obj).forEach(([key, propertyValue]) => {
if (typeof propertyValue === "object" && propertyValue) {
const updatedKey = keyPrepender ? `${keyPrepender}.${key}` : key;
convertObjectToArray(propertyValue, updatedKey)
} else {
myArr.push({
key: keyPrepender ? `${keyPrepender}.${key}` : key,
propertyValue
})
}
})
}
convertObjectToArray(myObj);
console.log(myArr);
I'd probably take a recursive approach, and I'd probably avoid unnecessary intermediary arrays (though unless the original object is massive, it doesn't matter). For instance (see comments):
function convert(obj, target = [], prefix = "") {
// Loop through the object keys
for (const key in obj) {
// Only handle "own" properties
if (Object.hasOwn(obj, key)) {
const value = obj[key];
// Get the full key for this property, including prefix
const fullKey = prefix ? prefix + "." + key : key;
if (value && typeof value === "object") {
// It's an object...
if (Array.isArray(value)) {
throw new Error(`Arrays are not valid`);
} else {
// ...recurse, providing the key as the prefix
convert(value, target, fullKey);
}
} else {
// Not an object, push it to the array
target.push({key: fullKey, propertyValue: value});
}
}
}
// Return the result
return target;
}
Live Example:
const original = {
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
};
function convert(obj, target = [], prefix = "") {
// Loop through the object keys
for (const key in obj) {
// Only handle "own" properties
if (Object.hasOwn(obj, key)) {
const value = obj[key];
// Get the full key for this property, including prefix
const fullKey = prefix ? prefix + "." + key : key;
if (value && typeof value === "object") {
// It's an object...
if (Array.isArray(value)) {
throw new Error(`Arrays are not valid`);
} else {
// ...recurse, providing the key as the prefix
convert(value, target, fullKey);
}
} else {
// Not an object, push it to the array
target.push({key: fullKey, propertyValue: value});
}
}
}
// Return the result
return target;
}
const result = convert(original, []);
console.log(result);
.as-console-wrapper {
max-height: 100% !important;
}
Note that I've assumed the order of the array entries isn't significant. The output you said you wanted is at odds with the standard order of JavaScript object properties (yes, they have an order now; no, it's not something I suggest relying on 😀). I've gone ahead and not worried about the order within an object.
This is the most bulletproof I could do :D, without needing a global variable, you just take it, and us it wherever you want!
const test = {
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
};
function toPropertiesByPath(inputObj) {
let arr = [];
let initialObj = {};
const getKeys = (obj, parentK='') => {
initialObj = arr.length === 0 ? obj: initialObj;
const entries = Object.entries(obj);
for(let i=0; i<entries.length; i++) {
const key = entries[i][0];
const val = entries[i][1];
const isRootElement = initialObj.hasOwnProperty(key);
parentK = isRootElement ? key: parentK+'.'+key;
if(typeof val === 'object' && val!==null && !Array.isArray(val)){
getKeys(val, parentK);
} else {
arr.push({ key: parentK, property: val });
}
}
};
getKeys(inputObj);
return arr;
}
console.log(toPropertiesByPath(test));
I wrote a small version using recursive function and another for validation is an object.
let values = {
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
}
function isObject(obj) {
return obj != null && obj.constructor.name === "Object"
}
function getValues(values) {
let arrValues = Object.keys(values).map(
v => {
return { key: v, value: isObject(values[v]) ? getValues(values[v]) : values[v] };
});
console.log(arrValues);
}
getValues(values);

How to read objects with sub-objects

I have an object with several sub-objects and I would like to retrieve all elements.
When running the following code, I only retrieve part of the elements till the 'age'
var output = '';
var main_table = {
animal: 'dog',
color:'black',
age: {
year:2016,
month:11,
day:1
},
race:'sheepdog',
parents: {
father:'Dad',
mother:'Mom'
}
};
function test(main_table){
table=main_table;
for (var name in table) {
if (table.hasOwnProperty(name)) {
if (table[name]=="[object Object]") {
test(table[name]);
}
else {
output+=(name+' : '+table[name]+' ');
}
}
}
alert (output);
}
test(main_table)
Some help on it will be highly appreciated.
You had created an implicit global variable with this line:
table=main_table;
by missing out the var.
I have also refactored a little bit to return the output at each recursive stage, and alert at the end.
var main_table = {
animal: 'dog',
color:'black',
age:
{
year:2016,
month:11,
day:1
},
race:'sheepdog',
parents:
{
father:'Dad',
mother:'Mom'}
};
function test(main_table){
var table=main_table;
var output = '';
for (var name in table)
{
if (table.hasOwnProperty(name))
{
console.log(name, typeof table[name])
if (typeof table[name]== "object")
{
output+=test(table[name]);
}
else
{
output+=(name+' : '+table[name]+' ');
}
}
}
return output;
}
alert(test(main_table))
I suggest to use an iterative, over the keys and recursive, over the children, approach, with a proper check
if (object[key] !== null && typeof object[key] === 'object') { //...
for iterable objects.
Methods used:
Object.keys returns an array with own properties of the object
Array#forEach for iterating the array
function getElements(object) {
var result = [];
Object.keys(object).forEach(function (key) {
if (object[key] !== null && typeof object[key] === 'object') {
result = result.concat(getElements(object[key]));
return;
}
result.push([key, object[key]]);
});
return result;
}
var main_table = { animal: 'dog', color: 'black', age: { year: 2016, month: 11, day: 1 }, race: 'sheepdog', parents: { father: 'Dad', mother: 'Mom' } };
console.log(getElements(main_table));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Hi you set a wrong scope to your function because of this line table=main_table;
this code will work i suppose :
var output = '';
var main_table = {
animal: 'dog',
color:'black',
age:
{year:2016,month:11,day:1},
race:'sheepdog',
parents:
{father:'Dad',
mother:'Mom'}
};
function test(table){
for (var name in table)
{
if (table.hasOwnProperty(name))
{
if (table[name]=="[object Object]")
{
test(table[name]);
}
else
{
output+=(name+' : '+table[name]+' ');
}
}
}
alert(output);
}
test(main_table);

Flattening object to base level with ES6

How would you get this object:
const data = {
importantData: {
index: 0,
about: 'test',
':sub': {
index: 1
}
},
telephone: {
index: 2,
nr: 1234567
}
}
to this:
{
importantData: {
index: 0,
about: 'test'
},
':sub': {
index: 1
}
telephone: {
index: 2,
nr: 1234567
}
}
wtih ES6.
I had success with finding the '.sub' object and getting its properties. However, I could not find a way to delete it from 'importantData' object for what I created new object with assign and saved non-':sub' values.
Here is the unoptimized solution.
function flattenToOneLevel (data) {
for (let key in data) {
const selectorName = key;
const styles = data[key];
for (let _key in styles) {
if (Object.prototype.toString.call(styles[_key]) === '[object Object]') {
data[_key] = styles[_key];
delete data[key][_key];
}
}
}
return data;
}

How to see if given object matches one of the objects returned from firebase

I am trying to test if the given object matches the stored objects that are stored and returned from firebase. If it does then return true or store true in a variable.
Here is the example of given object and firebase returned data
given object:
{name: "John", age: "32"}
stored object:
{
'-JrYqLGTNLfa1zEkTG5J': { name: "John", age: "32" },
'-JrkhWMKHhrShX66dSWJ': { name: "Steve", age: "25" },
'-JrkhtHQPKRpNh0B0LqJ': { name: "Kelly", age: "33" },
'-JrkiItisvMJZP1fKsS8': { name: "Mike", age: "28" },
'-JrkiPqA8KyAMj2R7A2h': { name: "Liz", age: "22" }
}
var storedObject ={
'-JrYqLGTNLfa1zEkTG5J': { name: "John", age: "32" },
'-JrkhWMKHhrShX66dSWJ': { name: "Steve", age: "25" },
'-JrkhtHQPKRpNh0B0LqJ': { name: "Kelly", age: "33" },
'-JrkiItisvMJZP1fKsS8': { name: "Mike", age: "28" },
'-JrkiPqA8KyAMj2R7A2h': { name: "Liz", age: "22" }
};
var givenObject = {name: "John", age: "32"};
This is how you can check if objects are equal. You just have to loop the object
Javascript Way
for(key in storedObject){
if(JSON.stringify(storedObject[key]) === JSON.stringify(givenObject)){
alert('equal'+givenObject['name'] +givenObject['age']);
}
};
jQuery Way
using $.each() function
$.each(storedObject,function(key,value){
if(JSON.stringify(value) === JSON.stringify(givenObject)){
alert('equal'+givenObject['name'] +givenObject['age']);
}
});
Take a look at the FIDDLE LINK
Also For your info, check out this SO Object comparison in JavaScript LINK
For completeness here is a solution in iOS/MacOS
//query for all users
FQuery *allUsersRef = [usersRef queryOrderedByChild:#"name"];
//filter that query with a query for users named "John"
FQuery *justJohnRef = [allUsersRef queryEqualToValue:#"John"];
//read in all of the resulting users named John
[justJohnRef observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
NSArray *johnArray = [snapshot.value allObjects];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"age == %#", #"32"];
NSArray *result = [johnArray filteredArrayUsingPredicate:predicate];
NSLog(#"%#", result);
}];
The concept here is that we use a broad query to bring in a range of data that we need, in this case all users named John, then filter that data in code for all of the John users returned that are age 32.
Note that if there are more than one John age 32 the result array will contain all of them. More detailed info should be supplied to the NSPredicate to get the exact John you are looking for (i.e. SSN = x or Drivers License = y)
Here is a generic example using reusable code (ES5 compliant environment required), so it is not limited to just the data that you have provided.
// some generic reuseable code
(function () {
'use strict';
function $isPrimitive(inputArg) {
var type = typeof inputArg;
return type === 'undefined' || inputArg === null || type === 'boolean' || type === 'string' || type === 'number' || type === 'symbol';
}
function $isFunction(inputArg) {
return typeof inputArg === 'function';
}
function $isDate(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object Date]';
}
function $isRegExp(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object RegExp]';
}
function $isString(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object String]';
}
function $isArguments(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object Arguments]';
}
function $getItem(object, index) {
var item;
if ($isString(object)) {
item = object.charAt(index);
} else {
item = object[index];
}
return item;
}
var de = function (a, b, circ) {
if (a === b) {
return true;
}
var aType,
bType,
aIsArgs,
bIsArgs,
aIsPrim,
bIsPrim,
aCirc,
bCirc,
ka,
kb,
length,
index,
it;
if ($isDate(a) && $isDate(b)) {
return a.getTime() === b.getTime();
}
if ($isRegExp(a) && $isRegExp(b)) {
return a.source === b.source &&
a.global === b.global &&
a.multiline === b.multiline &&
a.lastIndex === b.lastIndex &&
a.ignoreCase === b.ignoreCase &&
a.sticky === b.sticky;
}
aIsPrim = $isPrimitive(a);
bIsPrim = $isPrimitive(b);
if ((aIsPrim || $isFunction(a)) && (bIsPrim || $isFunction(b))) {
/*jslint eqeq: true */
return a == b;
}
if (aIsPrim || bIsPrim) {
return a === b;
}
if (a.prototype !== b.prototype) {
return false;
}
if (circ.a.indexOf(a) === -1) {
circ.a.push(a);
} else {
aCirc = true;
}
if (circ.b.indexOf(b) === -1) {
circ.b.push(b);
} else {
bCirc = true;
}
if (aCirc && bCirc) {
circ.cnt += 1;
} else {
circ.cnt = 0;
}
if (circ.cnt > 200) {
throw new RangeError('Circular reference limit exceeded');
}
aIsArgs = $isArguments(a);
bIsArgs = $isArguments(b);
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) {
return false;
}
if (aIsArgs) {
return de(Array.prototype.slice.call(a), Array.prototype.slice.call(b), circ);
}
ka = Object.keys(a);
kb = Object.keys(b);
length = ka.length;
if (length !== kb.length) {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
} else {
return false;
}
} else {
ka.sort();
kb.sort();
for (index = 0; index < length; index += 1) {
if (ka[index] !== kb[index]) {
return false;
}
}
}
for (index = 0; index < length; index += 1) {
it = ka[index];
if (!de($getItem(a, it), $getItem(b, it), circ)) {
return false;
}
}
aType = typeof a;
bType = typeof b;
return aType === bType;
};
if (!Object.prototype.deepEqual) {
Object.defineProperty(Object.prototype, 'deepEqual', {
enumerable: false,
configurable: true,
writable: true,
value: function (b) {
var a = this;
return de(a, b, {
a: [],
b: [],
cnt: 0
});
}
});
}
if (!Object.prototype.forKeys) {
Object.defineProperty(Object.prototype, 'forKeys', {
enumerable: false,
configurable: true,
writable: true,
value: function (fn, thisArg) {
var object = Object(this),
keys,
length,
val,
index,
it;
if (!$isFunction(fn)) {
throw new TypeError('Argument is not a function: ' + fn);
}
keys = Object.keys(object);
length = keys.length;
val = false;
for (index = 0; index < length; index += 1) {
it = keys[index];
val = !!fn.call(thisArg, $getItem(object, it), it, object);
if (val) {
break;
}
}
return val;
}
});
}
}());
// example of use with your data
var stored = {
'-JrYqLGTNLfa1zEkTG5J': {
name: "John",
age: "32"
},
'-JrkhWMKHhrShX66dSWJ': {
name: "Steve",
age: "25"
},
'-JrkhtHQPKRpNh0B0LqJ': {
name: "Kelly",
age: "33"
},
'-JrkiItisvMJZP1fKsS8': {
name: "Mike",
age: "28"
},
'-JrkiPqA8KyAMj2R7A2h': {
name: "Liz",
age: "22"
}
},
given = {
name: "John",
age: "32"
},
found = stored.forKeys(function (item) {
return item.deepEqual(this);
}, given);
document.getElementById('out').textContent = 'given was matched in stored: ' + found;
<pre id="out"></pre>

Get string representation of JavaScript object

I have a object like so:
$scope.query = {
filter: {
column: {
productName: 'Some Product',
price: 29.95,
...
},
table: {
productType: 'GM',
categoryId: 1,
...
}
}
};
How do I get a string that represents the whole object in dot notation? e.g.
query.filter.table.productType
To clarify, I am using this string value as a key to store a key/value pair in localStorage.
I am using angular to $wacth each property on the object for a change. Since you can't watch an object and know which property changed with watching all, I need to get creative and store each property in a key/value pair.
You can do it recursively, and produces "key" in an array.
var obj = {
query: {
filter: {
table: {
productType: 'GM'
}
}
}
};
var stringify = function (e) {
var rs = [];
for (var k in e) {
if (e.hasOwnProperty(k)) {
if (typeof e[k] == 'object') {
var l = stringify(e[k]);
for (var i = 0; i < l.length; i++) {
rs.push(k + '.' + l[i]);
}
} else {
rs.push(k);
}
}
}
return rs;
}
console.log(stringify(obj));
outputs:
["query.filter.table.productType"]
fiddle
Demo
Before Ques Edit
var $scope = {
query: {
filter: {
table: {
productType: 'GM'
}
}
}
};
var k = JSON.stringify($scope)
//output "{"query":{"filter":{"table":{"productType":"GM"}}}}"
k.match(/\w+(?=\"\:)/g).join('.')
//output"query.filter.table.productType"
Edit
Updated Demo
If OP has no issue with the position of child elements
var $scope = {}
$scope.query = {
filter: {
column: {
productName: 'Some Product',
price: 29.95
},
table: {
productType: 'GM',
categoryId: 1,
}
}
};
k=JSON.stringify($scope)
{"query":{"filter":{"column":{"productName":"Some Product","price":29.95},"table":{"productType":"GM","categoryId":1}}}}
k.match(/\w+(?=\"\:)/g).join('.')
"query.filter.column.productName.price.table.productType.categoryId"
By iterating the properties into an array recursively you could create a hierarchical structure that represents the data in the object. From here you could parse the results out as you wish.
var scope = {
query: {
filter: {
column: {
productName: 'Some Product',
price: 29.95
},
table: {
productType: 'GM',
categoryId: 1
}
}
}
};
function buildProps(subject) {
var result = [];
for (var key in subject) {
if (subject.hasOwnProperty(key)) {
if (typeof subject[key] == "object") {
result.push(key, buildProps(subject[key]));
} else {
result.push(key);
}
}
}
return result;
}
function stringify(input) {
var result = [];
for (var i = 0; i < input.length; i++) {
if (typeof input[i] == "string") {
result.push(input[i]);
} else {
result = result.concat(stringify(input[i]));
}
}
return result.join('.');
}
console.log(buildProps(scope));
console.log(stringify(buildProps(scope)));
Parse out the strings in the resulting array/sub-arrays, format it any way you like.
In my simple example I just list them in order:
query.filter.column.productName.price.table.productType.categoryId

Categories