Array of key-value pair JSON object parsing - javascript

Hi I have have a given array of JSON object in a file:
file.json:
[
{
"id": "xccdf_saphana.content_profile_test1",
"info": {
"applicable_platforms": ["SUSE", "RHEL", "SUSE FOR SAP APP"],
"applicable_workloads": "SalesPromo",
"applicable_compliance": "CIS",
"type":"System"
}
},
{
"id": "xccdf_saphana.content_profile_test2",
"info": {
"applicable_workloads": "SalesPromo",
"applicable_compliance": "CIS",
"type":"System"
}
}
]
Below is the way I am reading it.
var obj = JSON.parse(fs.readFileSync(file.json, 'utf8')); // read the file
myID = "xccdf_saphana.content_profile_test2";
var myInfo = getInfobyID(myID);
function getInfobyID(myID) {
// Got messed up, tried changing JSON structure multiple time, so that I can easily parse it.
for(var i=0; i < obj.length; ++i) {
if(obj[i].id == myID)
return obj[i].info;
}
}
Is their any way I can optimize it, as I will be recursively searching for multiple myID later.

Turn your json into an object rather than an array. Then you can make quick lookups.
let hash = obj.reduce((agg, e) => {
agg[e.id] = e;
return agg;
}, {});
let value = hash["xccdf_saphana.content_profile_test2"].info;

The naming 'obj' is a bit confusing here since it is actually an array of objects (from the JSON file).
Try something like:
var myArrOfObj = JSON.parse(fs.readFileSync(file.json, 'utf8'));
var myID = "xccdf_saphana.content_profile_test2";
var myInfo = getInfobyID(myID);
function getInfobyID(myID){
var matchingObj = myArrOfObj.find((obj) => obj.id === myID);
// Fallback to empty string since there might be no match.
return (matchingObj) ? matchingObj.info : '';
}

You can use Array.reduce to convert your array into an object with key as id and value as info.
Now you can just return the object[id] and get the info without iterating everytime.
var json = [{
"id": "xccdf_saphana.content_profile_test1",
"info": {
"applicable_platforms": ["SUSE", "RHEL", "SUSE FOR SAP APP"],
"applicable_workloads": "SalesPromo",
"applicable_compliance": "CIS",
"type": "System"
}
},
{
"id": "xccdf_saphana.content_profile_test2",
"info": {
"applicable_workloads": "SalesPromo",
"applicable_compliance": "CIS",
"type": "System"
}
}
];
var data = json.reduce((acc, curr) => {
acc[curr.id] = curr.info;
return acc;
}, {});
function getInfobyID(data, id) {
return data[id];
}
console.log(getInfobyID(data, "xccdf_saphana.content_profile_test2"));

Based on this requirement:
Is their any way I can optimize it...
You may want to store the ID value as the key of a map, and any info related to it as the value. This way, whenever you are searching, if you already have the ID yo can access the data for that ID in constant time, or O(1), as opposed to linear time, or O(n).
This is the only way to speed up your search without more complex data structures, but it comes with one caveat which is that you no longer have a list. You will be using a map.
Change this:
[
{
"id": "xccdf_saphana.content_profile_test1",
"info": {
"applicable_platforms": ["SUSE","RHEL","SUSE FOR SAP APP"],
"applicable_workloads": "SalesPromo",
"applicable_compliance": "CIS",
"type":"System" }
},
{
"id": "xccdf_saphana.content_profile_test2",
"info": {
"applicable_workloads": "SalesPromo",
"applicable_compliance": "CIS",
"type":"System" }
}
]
To this:
{
"xccdf_saphana.content_profile_test1": {
"applicable_platforms": ["SUSE","RHEL","SUSE FOR SAP APP"],
"applicable_workloads": "SalesPromo",
"applicable_compliance": "CIS",
"type":"System"
},
"xccdf_saphana.content_profile_test2": {
"applicable_workloads": "SalesPromo",
"applicable_compliance": "CIS",
"type":"System"
}
}
Now you don't need any loops. You just have one object, with each member of the object representing a different item.
In your code, you can simply do obj[myId] and you will either get undefined if it doesn't exist, or you will get an object of the matching result.
This is the fastest a search could possibly be, but again it requires a map-like data structure and not a list.
If you absolutely must use a list, the only real optimization to be made is as follows:
Cache your list length, so you do not have to calculate it on each iteration of the loop
Your new getInfobyID could look like this:
function getInfobyID(myID){
var len = obj.length;
for (var i=0; i < len; ++i){
if( obj[i].id == myID)
return obj[i].info;
}
}

Related

Cannot read property of length undefined

I'm using a dexi.io robot to automate extraction from permit databases. The robot will accept custom javascript to parse the incoming JSON object.
This code appears to work in some cases - correctly parsing the incoming JSON - but fails in almost every circumstance. The error is cannot read property of length undefined.
Here's the test api that works: https://services.odata.org/V4/TripPinService/People
Here's the code:
var people = JSON.parse(json).value;
var arr = [];
function getCountry(member) {
try {
return member.fields.name;
} catch(err) {
return "";
}
}
for (i = 0; i < people.length; i++) {
var member = people[i];
var obj =
{
"name": member.name,
"Country": getCountry(member),
"alias": member.alias
};
arr.push(obj);
}
return arr;
Check your first line where is "json".I think you didn't defined a json file from which you are getting data.
Not sure exactly what you are doing to fetch the response, but I was able to get this working pretty easily:
fetch("https://services.odata.org/V4/TripPinService/People")
.then(response => response.json())
.then(data => {
var people = data.value;
var arr = [];
function getCountry(member) {
try {
return member.AddressInfo[0].City.CountryRegion;
} catch(err) {
return "Country Info Not Available";
}
}
for (i = 0; i < people.length; i++) {
var member = people[i];
var obj =
{
"name": member.FirstName,
"Country": getCountry(member),
"alias": member.alias
};
arr.push(obj);
}
console.log(arr);
});
Maybe this will give you some ideas.
I have checked the json from the given url. So, in your code the properties like 'member.name', 'member.alias', 'member.fields.name' doesn't exist.
Try using the exact property names from the json:
{
"#odata.context": "http://services.odata.org/V4/TripPinService/$metadata#People",
"#odata.nextLink": "https://services.odata.org/V4/TripPinService/People?%24skiptoken=8",
"value": [
{
"#odata.id": "http://services.odata.org/V4/TripPinService/People('russellwhyte')",
"#odata.etag": "W/\"08D817F8EC7DAC16\"",
"#odata.editLink": "http://services.odata.org/V4/TripPinService/People('russellwhyte')",
"UserName": "russellwhyte",
"FirstName": "Russell",
"LastName": "Whyte",
"Emails": [
"Russell#example.com",
"Russell#contoso.com"
],
"AddressInfo": [
{
"Address": "187 Suffolk Ln.",
"City": {
"CountryRegion": "United States",
"Name": "Boise",
"Region": "ID"
}
}
],
"Gender": "Male",
"Concurrency": 637285705159912400
}
]
}
What I am trying to say is that use the names exactly in json like 'member.UserName', 'member.AddressInfo[0].Name' etc.

Javascript - how to loop through dict inside a list

So I am pretty new when it comes to Javascript and it is as simple as read a json list with a value of:
{
"URL": [{
"https://testing.com/en/p/-12332423/": "999"
}, {
"https://testing.com/en/p/-123456/": "123"
},
{
"https://testing.com/en/p/-456436346/": "422"
}
]
}
What I would like to do is to have both the URL and the amount of numbers etc
"https://testing.com/en/p/-12332423/" and "999"
and I would like to for loop so it runs each "site" one by one so the first loop should be
"https://testing.com/en/p/-12332423/" and "999"
second loop should be:
"https://testing.com/en/p/-123456/" and "123"
and so on depending on whats inside the json basically.
So my question is how am I able to loop it so I can use those values for each loop?
As Adam Orlov pointed out in the coment, Object.entries() can be very useful here.
const URLobj = {
"URL": [{
"https://testing.com/en/p/-12332423/": "999"
}, {
"https://testing.com/en/p/-123456/": "123"
},
{
"https://testing.com/en/p/-456436346/": "422"
}
]
};
URLobj.URL.forEach(ob => {
console.log('ob', ob);
const entries = Object.entries(ob)[0]; // 0 just means the first key-value pair, but because each object has only one we can just use the first one
const url = entries[0];
const number = entries[1];
console.log('url', url);
console.log('number', number);
})
You mean something like this using Object.entries
const data = {
"URL": [
{"https://testing.com/en/p/-12332423/": "999"},
{"https://testing.com/en/p/-123456/": "123"},
{"https://testing.com/en/p/-456436346/": "422"}
]
}
data.URL.forEach(obj => { // loop
const [url, num] = Object.entries(obj)[0]; // grab the key and value from each entry - note the [0]
console.log("Url",url,"Number", num); // do something with them
})
let's call your object o1 for simplicity. So you can really go to town with this link - https://zellwk.com/blog/looping-through-js-objects/
or you can just use this code :
for(var i = 0; i < o1.URL.length; i++) {
//each entry
var site = Object.keys(URL[i]) [0];
var value = Object.values(URL[i]) [0];
// ... do whatever
}
don't forget each member of the array is an object (key : value) in its own right
You can extract the keys and their values into another object array using map
Then use the for loop on the newly created array. You can use this method on any object to separate their keys and values into another object array.
const data = {
"URL": [{
"https://testing.com/en/p/-12332423/": "999"
}, {
"https://testing.com/en/p/-123456/": "123"
},
{
"https://testing.com/en/p/-456436346/": "422"
}
]
}
var extracted = data.URL.map(e => ({
url: Object.keys(e)[0],
number: Object.values(e)[0]
}))
extracted.forEach((e) => console.log(e))

Getting a list of unique fields within a collection including nested [duplicate]

I'd like to get the names of all the keys in a MongoDB collection.
For example, from this:
db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { egg : ['cat'] } );
db.things.insert( { type : [] } );
db.things.insert( { hello : [] } );
I'd like to get the unique keys:
type, egg, hello
You could do this with MapReduce:
mr = db.runCommand({
"mapreduce" : "my_collection",
"map" : function() {
for (var key in this) { emit(key, null); }
},
"reduce" : function(key, stuff) { return null; },
"out": "my_collection" + "_keys"
})
Then run distinct on the resulting collection so as to find all the keys:
db[mr.result].distinct("_id")
["foo", "bar", "baz", "_id", ...]
With Kristina's answer as inspiration, I created an open source tool called Variety which does exactly this: https://github.com/variety/variety
You can use aggregation with the new $objectToArray aggregation operator in version 3.4.4 to convert all top key-value pairs into document arrays, followed by $unwind and $group with $addToSet to get distinct keys across the entire collection. (Use $$ROOT for referencing the top level document.)
db.things.aggregate([
{"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
{"$unwind":"$arrayofkeyvalue"},
{"$group":{"_id":null,"allkeys":{"$addToSet":"$arrayofkeyvalue.k"}}}
])
You can use the following query for getting keys in a single document.
db.things.aggregate([
{"$match":{_id: "<<ID>>"}}, /* Replace with the document's ID */
{"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
{"$project":{"keys":"$arrayofkeyvalue.k"}}
])
A cleaned up and reusable solution using pymongo:
from pymongo import MongoClient
from bson import Code
def get_keys(db, collection):
client = MongoClient()
db = client[db]
map = Code("function() { for (var key in this) { emit(key, null); } }")
reduce = Code("function(key, stuff) { return null; }")
result = db[collection].map_reduce(map, reduce, "myresults")
return result.distinct('_id')
Usage:
get_keys('dbname', 'collection')
>> ['key1', 'key2', ... ]
If your target collection is not too large, you can try this under mongo shell client:
var allKeys = {};
db.YOURCOLLECTION.find().forEach(function(doc){Object.keys(doc).forEach(function(key){allKeys[key]=1})});
allKeys;
If you are using mongodb 3.4.4 and above then you can use below aggregation using $objectToArray and $group aggregation
db.collection.aggregate([
{ "$project": {
"data": { "$objectToArray": "$$ROOT" }
}},
{ "$project": { "data": "$data.k" }},
{ "$unwind": "$data" },
{ "$group": {
"_id": null,
"keys": { "$addToSet": "$data" }
}}
])
Here is the working example
Try this:
doc=db.thinks.findOne();
for (key in doc) print(key);
Using python. Returns the set of all top-level keys in the collection:
#Using pymongo and connection named 'db'
reduce(
lambda all_keys, rec_keys: all_keys | set(rec_keys),
map(lambda d: d.keys(), db.things.find()),
set()
)
Here is the sample worked in Python:
This sample returns the results inline.
from pymongo import MongoClient
from bson.code import Code
mapper = Code("""
function() {
for (var key in this) { emit(key, null); }
}
""")
reducer = Code("""
function(key, stuff) { return null; }
""")
distinctThingFields = db.things.map_reduce(mapper, reducer
, out = {'inline' : 1}
, full_response = True)
## do something with distinctThingFields['results']
I am surprise, no one here has ans by using simple javascript and Set logic to automatically filter the duplicates values, simple example on mongo shellas below:
var allKeys = new Set()
db.collectionName.find().forEach( function (o) {for (key in o ) allKeys.add(key)})
for(let key of allKeys) print(key)
This will print all possible unique keys in the collection name: collectionName.
I think the best way do this as mentioned here is in mongod 3.4.4+ but without using the $unwind operator and using only two stages in the pipeline. Instead we can use the $mergeObjects and $objectToArray operators.
In the $group stage, we use the $mergeObjects operator to return a single document where key/value are from all documents in the collection.
Then comes the $project where we use $map and $objectToArray to return the keys.
let allTopLevelKeys = [
{
"$group": {
"_id": null,
"array": {
"$mergeObjects": "$$ROOT"
}
}
},
{
"$project": {
"keys": {
"$map": {
"input": { "$objectToArray": "$array" },
"in": "$$this.k"
}
}
}
}
];
Now if we have a nested documents and want to get the keys as well, this is doable. For simplicity, let consider a document with simple embedded document that look like this:
{field1: {field2: "abc"}, field3: "def"}
{field1: {field3: "abc"}, field4: "def"}
The following pipeline yield all keys (field1, field2, field3, field4).
let allFistSecondLevelKeys = [
{
"$group": {
"_id": null,
"array": {
"$mergeObjects": "$$ROOT"
}
}
},
{
"$project": {
"keys": {
"$setUnion": [
{
"$map": {
"input": {
"$reduce": {
"input": {
"$map": {
"input": {
"$objectToArray": "$array"
},
"in": {
"$cond": [
{
"$eq": [
{
"$type": "$$this.v"
},
"object"
]
},
{
"$objectToArray": "$$this.v"
},
[
"$$this"
]
]
}
}
},
"initialValue": [
],
"in": {
"$concatArrays": [
"$$this",
"$$value"
]
}
}
},
"in": "$$this.k"
}
}
]
}
}
}
]
With a little effort, we can get the key for all subdocument in an array field where the elements are object as well.
This works fine for me:
var arrayOfFieldNames = [];
var items = db.NAMECOLLECTION.find();
while(items.hasNext()) {
var item = items.next();
for(var index in item) {
arrayOfFieldNames[index] = index;
}
}
for (var index in arrayOfFieldNames) {
print(index);
}
Maybe slightly off-topic, but you can recursively pretty-print all keys/fields of an object:
function _printFields(item, level) {
if ((typeof item) != "object") {
return
}
for (var index in item) {
print(" ".repeat(level * 4) + index)
if ((typeof item[index]) == "object") {
_printFields(item[index], level + 1)
}
}
}
function printFields(item) {
_printFields(item, 0)
}
Useful when all objects in a collection has the same structure.
To get a list of all the keys minus _id, consider running the following aggregate pipeline:
var keys = db.collection.aggregate([
{ "$project": {
"hashmaps": { "$objectToArray": "$$ROOT" }
} },
{ "$group": {
"_id": null,
"fields": { "$addToSet": "$hashmaps.k" }
} },
{ "$project": {
"keys": {
"$setDifference": [
{
"$reduce": {
"input": "$fields",
"initialValue": [],
"in": { "$setUnion" : ["$$value", "$$this"] }
}
},
["_id"]
]
}
}
}
]).toArray()[0]["keys"];
I know I am late to the party, but if you want a quick solution in python finding all keys (even the nested ones) you could do with a recursive function:
def get_keys(dl, keys=None):
keys = keys or []
if isinstance(dl, dict):
keys += dl.keys()
list(map(lambda x: get_keys(x, keys), dl.values()))
elif isinstance(dl, list):
list(map(lambda x: get_keys(x, keys), dl))
return list(set(keys))
and use it like:
dl = db.things.find_one({})
get_keys(dl)
if your documents do not have identical keys you can do:
dl = db.things.find({})
list(set(list(map(get_keys, dl))[0]))
but this solution can for sure be optimized.
Generally this solution is basically solving finding keys in nested dicts, so this is not mongodb specific.
Based on #Wolkenarchitekt answer: https://stackoverflow.com/a/48117846/8808983, I write a script that can find patterns in all keys in the db and I think it can help others reading this thread:
"""
Python 3
This script get list of patterns and print the collections that contains fields with this patterns.
"""
import argparse
import pymongo
from bson import Code
# initialize mongo connection:
def get_db():
client = pymongo.MongoClient("172.17.0.2")
db = client["Data"]
return db
def get_commandline_options():
description = "To run use: python db_fields_pattern_finder.py -p <list_of_patterns>"
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-p', '--patterns', nargs="+", help='List of patterns to look for in the db.', required=True)
return parser.parse_args()
def report_matching_fields(relevant_fields_by_collection):
print("Matches:")
for collection_name in relevant_fields_by_collection:
if relevant_fields_by_collection[collection_name]:
print(f"{collection_name}: {relevant_fields_by_collection[collection_name]}")
# pprint(relevant_fields_by_collection)
def get_collections_names(db):
"""
:param pymongo.database.Database db:
:return list: collections names
"""
return db.list_collection_names()
def get_keys(db, collection):
"""
See: https://stackoverflow.com/a/48117846/8808983
:param db:
:param collection:
:return:
"""
map = Code("function() { for (var key in this) { emit(key, null); } }")
reduce = Code("function(key, stuff) { return null; }")
result = db[collection].map_reduce(map, reduce, "myresults")
return result.distinct('_id')
def get_fields(db, collection_names):
fields_by_collections = {}
for collection_name in collection_names:
fields_by_collections[collection_name] = get_keys(db, collection_name)
return fields_by_collections
def get_matches_fields(fields_by_collections, patterns):
relevant_fields_by_collection = {}
for collection_name in fields_by_collections:
relevant_fields = [field for field in fields_by_collections[collection_name] if
[pattern for pattern in patterns if
pattern in field]]
relevant_fields_by_collection[collection_name] = relevant_fields
return relevant_fields_by_collection
def main(patterns):
"""
:param list patterns: List of strings to look for in the db.
"""
db = get_db()
collection_names = get_collections_names(db)
fields_by_collections = get_fields(db, collection_names)
relevant_fields_by_collection = get_matches_fields(fields_by_collections, patterns)
report_matching_fields(relevant_fields_by_collection)
if __name__ == '__main__':
args = get_commandline_options()
main(args.patterns)
As per the mongoldb documentation, a combination of distinct
Finds the distinct values for a specified field across a single collection or view and returns the results in an array.
and indexes collection operations are what would return all possible values for a given key, or index:
Returns an array that holds a list of documents that identify and describe the existing indexes on the collection
So in a given method one could do use a method like the following one, in order to query a collection for all it's registered indexes, and return, say an object with the indexes for keys (this example uses async/await for NodeJS, but obviously you could use any other asynchronous approach):
async function GetFor(collection, index) {
let currentIndexes;
let indexNames = [];
let final = {};
let vals = [];
try {
currentIndexes = await collection.indexes();
await ParseIndexes();
//Check if a specific index was queried, otherwise, iterate for all existing indexes
if (index && typeof index === "string") return await ParseFor(index, indexNames);
await ParseDoc(indexNames);
await Promise.all(vals);
return final;
} catch (e) {
throw e;
}
function ParseIndexes() {
return new Promise(function (result) {
let err;
for (let ind in currentIndexes) {
let index = currentIndexes[ind];
if (!index) {
err = "No Key For Index "+index; break;
}
let Name = Object.keys(index.key);
if (Name.length === 0) {
err = "No Name For Index"; break;
}
indexNames.push(Name[0]);
}
return result(err ? Promise.reject(err) : Promise.resolve());
})
}
async function ParseFor(index, inDoc) {
if (inDoc.indexOf(index) === -1) throw "No Such Index In Collection";
try {
await DistinctFor(index);
return final;
} catch (e) {
throw e
}
}
function ParseDoc(doc) {
return new Promise(function (result) {
let err;
for (let index in doc) {
let key = doc[index];
if (!key) {
err = "No Key For Index "+index; break;
}
vals.push(new Promise(function (pushed) {
DistinctFor(key)
.then(pushed)
.catch(function (err) {
return pushed(Promise.resolve());
})
}))
}
return result(err ? Promise.reject(err) : Promise.resolve());
})
}
async function DistinctFor(key) {
if (!key) throw "Key Is Undefined";
try {
final[key] = await collection.distinct(key);
} catch (e) {
final[key] = 'failed';
throw e;
}
}
}
So querying a collection with the basic _id index, would return the following (test collection only has one document at the time of the test):
Mongo.MongoClient.connect(url, function (err, client) {
assert.equal(null, err);
let collection = client.db('my db').collection('the targeted collection');
GetFor(collection, '_id')
.then(function () {
//returns
// { _id: [ 5ae901e77e322342de1fb701 ] }
})
.catch(function (err) {
//manage your error..
})
});
Mind you, this uses methods native to the NodeJS Driver. As some other answers have suggested, there are other approaches, such as the aggregate framework. I personally find this approach more flexible, as you can easily create and fine-tune how to return the results. Obviously, this only addresses top-level attributes, not nested ones.
Also, to guarantee that all documents are represented should there be secondary indexes (other than the main _id one), those indexes should be set as required.
We can achieve this by Using mongo js file. Add below code in your getCollectionName.js file and run js file in the console of Linux as given below :
mongo --host 192.168.1.135 getCollectionName.js
db_set = connect("192.168.1.135:27017/database_set_name"); // for Local testing
// db_set.auth("username_of_db", "password_of_db"); // if required
db_set.getMongo().setSlaveOk();
var collectionArray = db_set.getCollectionNames();
collectionArray.forEach(function(collectionName){
if ( collectionName == 'system.indexes' || collectionName == 'system.profile' || collectionName == 'system.users' ) {
return;
}
print("\nCollection Name = "+collectionName);
print("All Fields :\n");
var arrayOfFieldNames = [];
var items = db_set[collectionName].find();
// var items = db_set[collectionName].find().sort({'_id':-1}).limit(100); // if you want fast & scan only last 100 records of each collection
while(items.hasNext()) {
var item = items.next();
for(var index in item) {
arrayOfFieldNames[index] = index;
}
}
for (var index in arrayOfFieldNames) {
print(index);
}
});
quit();
Thanks #ackuser
Following the thread from #James Cropcho's answer, I landed on the following which I found to be super easy to use. It is a binary tool, which is exactly what I was looking for:
mongoeye.
Using this tool it took about 2 minutes to get my schema exported from command line.
I know this question is 10 years old but there is no C# solution and this took me hours to figure out. I'm using the .NET driver and System.Linq to return a list of the keys.
var map = new BsonJavaScript("function() { for (var key in this) { emit(key, null); } }");
var reduce = new BsonJavaScript("function(key, stuff) { return null; }");
var options = new MapReduceOptions<BsonDocument, BsonDocument>();
var result = await collection.MapReduceAsync(map, reduce, options);
var list = result.ToEnumerable().Select(item => item["_id"].ToString());
This one lines extracts all keys from a collection into a comma separated sorted string:
db.<collection>.find().map((x) => Object.keys(x)).reduce((a, e) => {for (el of e) { if(!a.includes(el)) { a.push(el) } }; return a}, []).sort((a, b) => a.toLowerCase() > b.toLowerCase()).join(", ")
The result of this query typically looks like this:
_class, _id, address, city, companyName, country, emailId, firstName, isAssigned, isLoggedIn, lastLoggedIn, lastName, location, mobile, printName, roleName, route, state, status, token
I extended Carlos LM's solution a bit so it's more detailed.
Example of a schema:
var schema = {
_id: 123,
id: 12,
t: 'title',
p: 4.5,
ls: [{
l: 'lemma',
p: {
pp: 8.9
}
},
{
l: 'lemma2',
p: {
pp: 8.3
}
}
]
};
Type into the console:
var schemafy = function(schema, i, limit) {
var i = (typeof i !== 'undefined') ? i : 1;
var limit = (typeof limit !== 'undefined') ? limit : false;
var type = '';
var array = false;
for (key in schema) {
type = typeof schema[key];
array = (schema[key] instanceof Array) ? true : false;
if (type === 'object') {
print(Array(i).join(' ') + key+' <'+((array) ? 'array' : type)+'>:');
schemafy(schema[key], i+1, array);
} else {
print(Array(i).join(' ') + key+' <'+type+'>');
}
if (limit) {
break;
}
}
}
Run:
schemafy(db.collection.findOne());
Output
_id <number>
id <number>
t <string>
p <number>
ls <object>:
0 <object>:
l <string>
p <object>:
pp <number>
I was trying to write in nodejs and finally came up with this:
db.collection('collectionName').mapReduce(
function() {
for (var key in this) {
emit(key, null);
}
},
function(key, stuff) {
return null;
}, {
"out": "allFieldNames"
},
function(err, results) {
var fields = db.collection('allFieldNames').distinct('_id');
fields
.then(function(data) {
var finalData = {
"status": "success",
"fields": data
};
res.send(finalData);
delteCollection(db, 'allFieldNames');
})
.catch(function(err) {
res.send(err);
delteCollection(db, 'allFieldNames');
});
});
After reading the newly created collection "allFieldNames", delete it.
db.collection("allFieldNames").remove({}, function (err,result) {
db.close();
return;
});
I have 1 simpler work around...
What you can do is while inserting data/document into your main collection "things" you must insert the attributes in 1 separate collection lets say "things_attributes".
so every time you insert in "things", you do get from "things_attributes" compare values of that document with your new document keys if any new key present append it in that document and again re-insert it.
So things_attributes will have only 1 document of unique keys which you can easily get when ever you require by using findOne()

How to parse all JSON strings in a JavaScript object

I have 1 JavaScript object like this:
result = {
"status": "success",
"message": "Get successful!",
"data": {
"name":"Hello world",
"school": {
"name":"LHP",
"address":"HCM"
},
"class": "[{\"text\":\"Math\",\"code\":\"math124\"},{\"text\":\"Libra\",\"code\":\"libra124\"}]",
"student": "{\"time_range\":{\"type\":\"select\",\"text\":\"Today\",\"value\":[{\"code\":\"in_today\",\"text\":\"In Today\"}]}}"
}
}
So I have to parse class and student separately:
result.data.class = JSON.parse(result.data.class);
result.data.student = JSON.parse(result.data.student);
Is there other way to parse the whole result variable or make this step shorter/better?
Thanks
You could loop through the data property's children and parse them.
for (var i = 0; i < Object.keys(result.data).length; i++) {
try {
result.data[Object.keys(result.data)[i]] = JSON.parse(result.data[Object.keys(result.data)[i]]);
} catch (error) {} // it's already JSON
}
But I'd only do that if you're sure you'll only ever have to deal with stringified JSON in the data property of your object.

Drill down into Javascript object using array

I have an array of strings which work as a 'path' to a certain property of a JavaScript object:
var car = {
"owner": "Joe",
"model": {
name: "Ford",
"manufacturer": {
"founder": "Henry Ford"
}
}
};
var address = ["model", "manufacturer", "founder"];
I want to write a function which will use that address to find the property of the object which lies at the end of the path, in the example, return "Henry Ford". I want the function to be able to handle addresses of arbitrary length. What would this function look like?
You can do it with .reduce()
var value = address.reduce(function(o, name) { return o && o[name]; }, car);
The .reduce() function starts off with an initial "accumulator" value, which will be the car object here. It iterates through the array, passing in each value and expecting the next accumulator value to be returned. Each step just looks up the property value and returns it, or the accumulator itself it it's falsey.
This will handle the case the search fails too. Plus, it's easy to understand from just looking at the code.
Input: Object, Array
Output: found key, or null otherwise
function searchObject(root, pathArray) {
var node = root;
for(var index in pathArray) {
key = pathArray[index];
if(key in node) {
node = node[key];
} else {
node = null;
break;
}
}
return node;
}
Example:
var car = {
"owner": "Joe",
"model": {
name: "Ford",
"manufacturer": {
"founder": "Henry Ford"
}
}
};
and
var address = ["model", "manufacturer", "founder"];
then searchObject(car, address) returns "Henry Ford".
Try ...
function getValue(ob, address) {
result = ob;
for (var i=0,len=address.length; i<len; i++) {
result = result[address[i]];
}
return result;
}
This should return the object or value at the end of the address list.

Categories