How do I sort a JSON by Date? - javascript

I'm trying to loop through a JSON and sort it by the date so I can see the latest date to the oldest date, and then write it to the file.
Here is my code
var reader = JSON.parse(fs.readFileSync('txt.json', 'utf8'));
function sortByDate(a, b) {
return new Date(a.lastUpdated).toJSON() - new Date(b.lastUpdated).toJSON();
}
reader.sort(sortByDate)
JSON Data Example
{
"Data": {
"Contents": [
{
"Key": [
"HelloTest"
],
"lastUpdated": [
"2019-10-25T10:30:50.558Z"
]
},
{
"Key": [
"TestHello"
],
"lastUpdated": [
"2019-03-26T10:30:50.558Z"
]
}
]
}
}

Here are a couple of errors I found in your code:
Your function name has a typo, it should be sortByDate and not sortbyDate.
You need top sort the inner json.Data.Contents array, not the outer json object.
You need to reference the first element of your lastUpdated arrays using lastUpdated[0].
Finally, you do not need to call toJSON() on the date objects in your sorting function, simply convert to date and return the difference.
Also your inner data fields are arrays, which seems strange for a Key and a lastUpdated value.
If you keep your fields as arrays, here is a working example showing how to sort the inner Data.Contents array by date:
const jsonString = `{
"Data": {
"Contents": [{
"Key": ["HelloTest"],
"lastUpdated": ["2019-10-25T10:30:50.558Z"]
}, {
"Key": ["TestHello"],
"lastUpdated": ["2019-03-26T10:30:50.558Z"]
}]
}
}`;
function sortByDate(a, b) {
return new Date(a.lastUpdated[0]) - new Date(b.lastUpdated[0]);
}
const json = JSON.parse(jsonString);
const defaultValue = { Data: { Contents: [] } };
const sortedContents = [...(json || defaultValue).Data.Contents].sort(sortByDate);
const output = { ...json, Data: { Contents: sortedContents } };
console.log(output);
If you change your fields to scalars, which I suggest, here is another example:
const jsonString = `{
"Data": {
"Contents": [{
"Key": "HelloTest",
"lastUpdated": "2019-10-25T10:30:50.558Z"
}, {
"Key": "TestHello",
"lastUpdated": "2019-03-26T10:30:50.558Z"
}]
}
}`;
function sortByDate(a, b) {
return new Date(a.lastUpdated) - new Date(b.lastUpdated);
}
const json = JSON.parse(jsonString);
const defaultValue = { Data: { Contents: [] } };
const sortedContents = [...(json || defaultValue).Data.Contents].sort(sortByDate);
const output = { ...json, Data: { Contents: sortedContents } };
console.log(output);

It looks like you're reading contents from a file, then needs to sort it by date, and then finally write it to a new file. If that is what you're going for, the following should help:
const fs = require('fs');
const path = require('path');
// JSON files can be requied in as objects without parsing
// `arrayOfStuff` will be the var name for `Contents`
const { Data: { Contents: arrayOfStuff } } = require('./data.json');
function sortByDate(el1, el2) {
// access the date from the object and turn into date object
let date1 = new Date(el1.lastUpdated[0]);
let date2 = new Date(el2.lastUpdated[0]);
// compare date objects in revers order to get newest to oldest
return (date2 - date1);
}
// sort `Contents` from the `Data` object and turn into JSON
const sortedContent = arrayOfStuff.sort(sortByDate);
const newDataObj = JSON.stringify({ Data: { Content: sortedContent }}, null, 2);
// create the fully qualified file path with `sortedByDate.json` as the file name
const filePath = path.resolve('./', 'sortedByDate.json');
// write to new file
fs.writeFile(filePath, newDataObj, (err) => {
if(err) {
console.log('Made an oopsie:', err);
}
console.log(`Success!, new JSON file located at: ${filePath}`);
}); // write to file

Related

How to return specified data in function? JavaScript

I have the following data structure in an array from Neo4j.
DATA: [
{
"keys": [
"J"
],
"length": 1,
"_fields": [
{
"identity": {
"low": 9,
"high": 0
},
"labels": [
"Journal"
],
"properties": {
"body": "123"
},
"elementId": "9"
}
],
"_fieldLookup": {
"J": 0
}
},
{
"keys": [
"J"
],
"length": 1,
"_fields": [
{
"identity": {
"low": 6,
"high": 0
},
"labels": [
"Journal"
],
"properties": {
"name": "Journal 221204",
"body": "<p>Test!</p>",
"lastEdit": "221204_03:53:02 PM",
"createdOn": "221204_03:45:33 PM"
},
"elementId": "6"
}
],
"_fieldLookup": {
"J": 0
}
}
]
I am trying to loop over the array, add specific data to a new array (finalList), and return the new array with this code.
// Allow require
import { createRequire } from "module";
const require = createRequire(import.meta.url);
require('dotenv').config()
var neo4j = require('neo4j-driver')
let finalList = [];
export default async function Database(query) {
const Neo4jUser = process.env.Neo4jUser;
const Neo4jPass = process.env.Neo4jPass;
const uri = "";
try {
const driver = neo4j.driver(uri, neo4j.auth.basic(Neo4jUser, Neo4jPass));
let session = driver.session();
let result = await session.run(query);
let records = Object.values(result)[0];
console.log(Object.keys(records).length);
if (Object.keys(records).length === 1) {
let record = Object.values(records)[0];
let fields = record._fields;
let fields2 = fields[0];
let properties = fields2.properties;
console.log(properties);
return properties;
};
if (Object.keys(records).length >= 2) {
let count = Object.keys(records).length;
console.log(2);
console.log(count);
let get_properties = async (records, countTimes) => {
let record = Object.values(records)[countTimes];
let fields = record._fields;
let fields2 = fields[0];
let properties = fields2.properties;
console.log(properties);
return properties;
};
let countTimes = 0;
while (countTimes < count) {
let node = get_properties(records, countTimes);
finalList.concat(node.then());
countTimes++
};
console.log(finalList);
console.log(`DATA: ${records}`);
return finalList
};
}
catch (err) {
if (err.name == "Neo4jError") {
node = "No Database found"
return node;
}
else {
console.log(err);
}
};
};
Every time I run the code, finalList is returned empty.
How can I add the appropriate data to an array and have it returned inside the function?
I understand that I am having a problem with the scope of finalArray inside the function, as I am not able to have finalList.concat "save" the data, but I am unable to find a solution to my problem after my research.
Also, can anyone suggest a more efficient way of sorting the data than what I have above?
Any help is appreciated!
The problem is that node.then() doesn't return anything, so there is nothing to concat. Instead you would have to either pass a callback to .then() which will handle concatenating the lists or use await to wait till the value returned from get_properties() is resolved and then concat the lists.
I would suggest something like this,
// Function to asynchronously concat the lists
const async_concat = async(oldList, value) => {
return oldList.concat(value);
}
// Make sure finalList is not const
var finalList = [];
while (countTimes < count) {
// await the value of node and asynchronously concat the lists
let node = await get_properties(records, countTimes);
finalList = await async_concat(finalList, node);
countTimes++
};

How to loop through objects and count unique values of key?

I have logs of json files which are objects that look like
{
"logs": [
{
"id": "12321321321321",
"email": "test#email.com",
"message": "ahahaha"
},
{
"id": "12321321312",
"email": "test#email.com",
"message": "hahahaha."
},
"id": "12321321321"
}
I need to return a new object that contains
{
"hello_id": outer id of the json file,
"array": [
{
"email": "test#me.com",
"total": 2
}
]
}
So far I am looping through the json files and have
jsonsInDirectory.forEach((file) => {
const fileData = fs.readFileSync(path.join("./logs", file), "utf8");
const jsonData = JSON.parse(fileData);
}
});
The key is "logs" and "id" and the values are the objects in the "logs" and the value of "id"
How can I count and return a new object at the same time?
You can try this approach: make a hash object that counts emails. Then just map it to an array of objects.
const data = {
logs: [{
id: "89004ef9-e825-4547-a83a-c9e9429e8f95",
email: "noah.sanchez#me.com",
message: "successfully handled skipped operation."
},
{
id: "89004ef9-e825-4547-a83a-c9e9429e8f95",
email: "noah.sanchez#me.com",
message: "successfully handled skipped operation."
},
{
id: "89004ef9-e825-4547-a83a-c9e9429e8f95",
email: "noname#me.com",
message: "successfully handled skipped operation."
}],
id: "56f83bed-3705-4115-9067-73930cbecbc0",
};
const emails = data.logs.reduce((acc, { email }) => {
acc[email] = (acc[email] ?? 0) + 1;
return acc;
}, {});
const tally = Object.entries(emails)
.map(([email, total]) => ({ email, total }));
const result = { logs_id: data.id, tally };
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0 }
When you do const jsonData = JSON.parse(fileData);, you get the file data as a JSON and knowing the struct of that JSON you can easily get the info.
I have created a example https://codesandbox.io/s/stackoverflow-logs-count-example-jys2vg?file=/src/index.js
It may not solve exactly wath you want.
To solve this problem with the most time efficiency, you can create a tally object by firstly creating a map of the occurences of each mail with the email as the key and no of occurences as the value, since this will take constant (O(1)) time to execute, afterwhich you can create the tally array from the map as given below
output = []
jsonsInDirectory.forEach((file) => {
const fileData = fs.readFileSync(path.join("./logs", file), "utf8");
const jsonData = JSON.parse(fileData);
var map = {}
jsonData.logs.forEach((log) => {
if(log.email in map){
map[log.email] += 1
}
else {
map[log.email] = 1
}
});
var tally = []
for(var email in map){
tally.push({email: email, total: map[email]})
}
output.push({logs_id: jsonData['id'], tally: tally});
})

Javascript conditionally adding the nested object using map() operator

I have a below JSON,
var original = {
"todos": [
{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [
{
"vpa": "log#bda",
"mccCode": "0000"
}
]
}
]
}
And am trying to add new data inside the nested array i.e., "vpainfo". I have tried using the below code and able to adding the new values inside "vpainfo".
var newdata = {"vpa":"first#bda","mccCode":"1111"};
var newObj =
Object.assign({}, original,
{
todos: original.todos.map(todoInfo=>(todoInfo.accountNo=="50190000")?[
...todoInfo.vpainfo,
newdata
]: todoInfo)
});
And the resulted object is,
{"todos":[[{"vpa":"log#bda","mccCode":"0000"},{"vpa":"first#bda","mccCode":"1111"}]]}
But few of the key and values(accountNo and name) are getting missed, how do we get the full object with the latest updated values?
You only return the array, not the actual object, hence the error.
var original = {
"todos": [
{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [
{
"vpa": "log#bda",
"mccCode": "0000"
}
]
}
]
}
const newdata = {"vpa":"first#bda","mccCode":"1111"};
const newObj = Object.assign({}, original,
{
todos: original.todos.map(todoInfo=>{
if(todoInfo.accountNo=="50190000"){
return {
...todoInfo,
vpainfo: [...todoInfo.vpainfo, newdata]
}
}
return todoInfo
})
});
console.log(newObj)
All those spread operators seem a little excessive...
If all you wanna do is add newdata to that existing array, then do that:
var original = {
"todos": [{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [{
"vpa": "log#bda",
"mccCode": "0000"
}]
}]
};
const newdata = {
"vpa": "first#bda",
"mccCode": "1111"
};
// Find the correct account.
const account = original.todos.filter(t => t.accountNo === '50190000')[0];
if (account) {
account.vpainfo.push(newdata);
}
console.log(original);

How to iterate through complex Json object and do something on each property which equals a certain value

I got the following problem,
I need to iterate through a big Json object ( child nodes consist of array's, strings and objects with at least 4-5 layers of depth in terms of nested properties ).
In some parts across the big Json file there is a specific object structure, it has a property named "erpCode". I need to scan the Json and find all the objects with that property, take the value use that code to ask a different API for details and once I get the details insert them into the object with the current 'erpCode'.
Just to clarify, in my case the parent node property name in the Json always equals the value in 'typeSysname' field which located on the same 'level' as the erpCode property.
A simple example :
{
"cars": [
{
"name": "X222",
"carType": {
"erpCode": "skoda",
"value": null,
"typeSysName": "carType"
}
}
],
"model": {
"year": 1999,
"details": {
"erpCode": "112"
"value": null,
"typeSysName": "details"
}
}
}
In this example I need to find 2 properties get the values skoda and 112 out of them and get the value and description data from a different API and set it into this Json in the right location.
P.S. Any chance there is a good npm package which can help me with that?
Edit:
I got a solution in C# from a few months ago which runs in a generic way on the Json and handles the complexity of the structure in a generic way.
But I now need to convert this into Javascript and I am a bit lost.
public static string TranslateDocErpCodes(string jsonString, string topRetailerSysName)
{
try
{
var doc = JObject.Parse(jsonString);
var erpCodeList = doc.SelectTokens("$..erpCode").ToList();
foreach (var erpCodeJToken in erpCodeList)
{
var value = erpCodeJToken?.Value<string>();
var erpCodeParent = erpCodeJToken?.Parent.Parent;
var erpCodeProperty = erpCodeParent?.Path.Split(".").Last();
var result =
_dataService.GetLovFromErpCode(topRetailerSysName, erpCodeProperty, value);
if (result == null)//reset lov obj
{
if (erpCodeParent?.Parent is JProperty prop)
prop.Value = JObject.FromObject(new LovObject { ErpCode = value });
}
else//set lov obj
{
result.ErpCode = value;
if (erpCodeParent?.Parent is JProperty prop)
prop.Value = JObject.FromObject(result);
}
}
return JsonConvert.SerializeObject(doc);
}
catch (Exception e)
{
throw new Exception("ErpConvert.TranslateDocErpCodes() : " + e);
}
}
mb something like;
function processObject(jsonData) {
for (prop in jsonData) {
if (jsonData.hasOwnProperty(prop)) {
// We get our prop
if (prop === 'code') {
let codeValue = jsonData[prop]
doSomeAsync(codeValue)
.then(response => {
jsonData[prop] = response;
})
}
let curValue = jsonData[prop];
if (Array.isArray(curValue)) {
// Loop through the array, if array element is an object, call processObject recursively.
processArray(curValue);
} else if (typeof curValue === 'object') {
processObject(curValue);
}
}
}
}
I took the answer from Aravindh as a starting point and managed to reach what seems to be a complete solution.
I will share it here,
async function convertErpCodes(jsonData, orgName, parentPropertyName){
for (let prop in jsonData) {
if (jsonData.hasOwnProperty(prop)) {
if (prop === 'erpCode') {
const erpCodeValue = jsonData[prop]
const req = {"query": {"erpCode": erpCodeValue, "orgName": orgName, "typeSysName": parentPropertyName}};
const result = await viewLookupErpService.findOne(req);
if(result)
return result;
}
const curValue = jsonData[prop];
if (Array.isArray(curValue)) {
for(let i in curValue){
const res = await convertErpCodes(curValue[i], orgName, prop);
}
} else if (curValue && typeof curValue === 'object') {
const response = await convertErpCodes(curValue, orgName, prop);
if(response){
jsonData[prop] = response;
}
}
}
}
}
P.S.
I set up the values only if I get a response from the third party API ( this is the reason for the result and response logic in the recursion.
I'd use object-scan and lodash.set in combination
// const objectScan = require('object-scan');
// const lodash = require('lodash');
const stats = { cars: [{ name: 'X222', carType: { erpCode: 'skoda', value: null, typeSysName: 'carType' } }], model: { year: 1999, details: { erpCode: '112', value: null, typeSysName: 'details' } } };
const entries = objectScan(['**.erpCode'], { rtn: 'entry' })(stats);
console.log(entries);
// => [ [ [ 'model', 'details', 'erpCode' ], '112' ], [ [ 'cars', 0, 'carType', 'erpCode' ], 'skoda' ] ]
// where you would query the external api and place results in entries
entries[0][1] = 'foo';
entries[1][1] = 'bar';
entries.forEach(([k, v]) => lodash.set(stats, k, v));
console.log(stats);
// => { cars: [ { name: 'X222', carType: { erpCode: 'bar', value: null, typeSysName: 'carType' } } ], model: { year: 1999, details: { erpCode: 'foo', value: null, typeSysName: 'details' } } }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
<script src="https://bundle.run/lodash#4.17.20"></script>
Disclaimer: I'm the author of object-scan

how to get multiple keys from nested JSON in javascript?

My JSON is like:
{
"boundaries": [
{
"boundary": {
"boundaryId": "45083021141",
"boundaryType": "USA_POSTCODE",
"boundaryRef": "B1"
}
}
],
"themes": [
{
"TheftCrimeTheme": {
"boundaryRef": "B1",
"individualValueVariable": [
{
"name": "2013 Theft Crime",
"description": "Theft Crime for 2013",
"count": 2080
}
]
}
},
{
"BurglaryCrimeTheme": {
"boundaryRef": "B1",
"individualValueVariable": [
{
"name": "2013 Burglary Crime",
"description": "Burglary Crime for 2013",
"count": 302
}
]
}
}
]
}
I want to get value of count to display in graph. As you can see in the above json, inside themes there are two keys i.e TheftCrimeTheme and BurglaryCrimeTheme. I want to get value of count in everycrimetheme. For this I did the below code:
$http.get("http://152.144.218.70:8080/USACrime/api/crimeAPI?city="+$scope.strCity+"&crimeType="+$scope.type1+"&years="+$scope.type+"&month="+$scope.type2).success(function (result) {
for(var i=0;i<result.themes.length;i++){
var crime={};
console.log("did",result.themes[i]);
var test2 = result.themes[i];
console.log("test2",test2);
var test = test2[Object.keys(test2)];
console.log("test",test);
crime.name = Object.keys(result.themes[i]);
console.log("CrimeName",crime.name);
crime.data = [];
var test1 = test.individualValueVariable[0].count;
console.log("test1",test1);
crime.data.push(test1);
crime_data.push(crime);
}
});
My agenda is to plot graph showing count of every year.To achieve this firstly I have to get multiple keys like TheftCrimeTheme, BurglaryCrimeTheme etc. Then after that i can access value of count in individualValueVariable.
when I used Object.keys() method , I got an error "undefined" when i console values of nameR. Please suggest me how should i do this?
This function recieves info (as the whole json), and theme as the theme which you want to get the count (ie.: "BurglaryCrimeTheme").
getThemeCrimesCount = (info, theme) => {
const record = info.themes.find(obj => Object.keys(obj)[0] == theme)[theme];
return record.individualValueVariable.reduce((a, b) => a += b.count, 0);
}
getThemeCrimesCount(info, "BurglaryCrimeTheme"); // 302
getThemeCrimesCount(info, "TheftCrimeTheme"); // 2080
Formatting this to separate the elements for clarity.
// Builds and returns URL with query string attached.
const buildURL = (uri, params) => {
let queryParams = Object.keys(params).map(function(k) {
return encodeURIComponent(k) + '=' + encodeURIComponent(params[k])
}).join('&');
return uri + '?' + queryParams;
};
// Parses returned JSON object.
const parseStatistics = (result) => {
// My assumption here is that you are receiving a JSON string, which
// would need to be parsed into an object before being used.
let result = JSON.parse(result);
// Set base object
let namR = {};
// Iterate through themes array.
result.themes.forEach(function(element) {
// Find the object name for current theme.
let type = Object.keys(element)[0];
// Find count for current theme.
let count = element[type].individualValueVariable.count;
// Set count.
namR[type] = count;
});
// Log object
console.log(namR);
};
// Set up url info.
let params = {
city: $scope.strCity,
crimeType: $scope.type1,
years: $scope.type,
month: $scope.type2
};
let baseURL = "http://152.144.218.70:8080/USACrime/api/crimeAPI";
// Execute request.
$http.get(buildURL(baseURL, params)).success(parseStatistics(response));

Categories