how to navigate nested objects of unknown depth? - javascript

Im making a notetaking app and Ive decided to store all the notes and structure in JSON file. On javascript, I get the JSON with AJAX, parse it and output it on the website.
My note structure is array of objects that can be nested, like this (if it is a note, it has a "content" attribute, if it is a folder, it has an array of objects (can be empty array too if the folder should me empty):
data {
entries = [
{
name: "Some note",
content: "This is a test note"
},
{
name: "folder",
children: [
{
name: "Bread recpie",
content: "Mix flour with water..."
},
{
name: "Soups",
children: [
{
name: "Pork soup",
content: "Add meat, onion..."
},
{
name: "Chicken soup"
content: "....."
}
]
}
]
}
]
}
To list the root directory, its simple, i just loop through the array as it only outputs the top-level records:
for (entry of data.entries) {
const li = document.createElement("li");
li.textContent = entry.name;
if (entry.children) {
li.className = "folder";
} else {
li.className = "file";
}
loop.appendChild(li);
}
But what about the folders? How should I proceed in listing the folders if the depth of nesting is unknown? And how do I target the specific folder? Should I add unique IDs to every object so i can filter the array with them? Or should I store some kind of depth information in a variable all the time?

You're making this more difficult for yourself by saving data to a JSON file. That is not a good approach. What you need to do is design a database schema appropriate for your data and create an API that outputs a predictable pattern of data that your client can work with.
I would suggest having a Folder resource and a Note resource linked through a one-to-many relationship. Each Folder resource can have many associated Note entries, but each Note has only one Folder that it is linked to. I suggest using an ORM, because most make it easy to eager load related data. For instance, if you choose Laravel you can use Eloquent, and then getting all notes for a folder is as easy as:
$folderWithNotes = Folder::with('notes')->where('name', 'school-notes')->get();
Knowing PHP is beside the point. You should still be able to see the logic of that.
If you create a database and build a server-side API to handle your data, you will end up with JSON on your client side that has a predictable format and is easy to work with.

Related

Eleventy and Netlify blog, with author relations

I have a blog type site that I've been working on, which is almost complete. I've used Eleventy and now I've hooked up Netlify CMS which required me to restructure some data.
Netlify CMS requires me to have separate files for the relation widget data, in this case authors. So I have an authors directory, which presently has 3 json files: jbloggs.json, etc, each object is flat, an example of one being:
./src/_data/authors/jbloggs.json
{
"key": "jbloggs",
"name": "Joe Bloggs",
// ... Removed for brevity
}
I initially created an array of objects and everything was working great:
./src/authors.json
[
{
"key": "jbloggs",
"name": "Joe Bloggs",
// ... Removed for brevity
},
{
"key": "user2",
"name": "User Two",
// ... Removed for brevity
}
]
What I need to do, is grab however many files are in my authors directory and add the objects from within each file to the array in my authors.json file. I've tried using json-concat, to no avail. I have fs and json-concat required in my eleventy file:
const files = [];
const {resolve} = require('path');
const source = resolve('./src/_data/authors');
const target = resolve('./src/authors.json');
fs.readdirSync(source).forEach((file) => {
files.push(file);
})
jsonConcat({ src: files, dest: target }, function (json) {
console.log(files); // returns an array of the correct filenames
console.log(target); // returns the path to the target file /Users/darrenlee/Desktop/WebApp/src/authors.json
console.log(json); // returns null
});
In my terminal I also get: [11ty] File changed: src/authors.json, but the file hasn't changed, I still only have 2 authors in there and the aim is to have all of the authors (currently 3) from the authors directory files.
Any help would be greatly appreciated.
Thank you
It turns out I was over-complicating things and I didn't need to add to the authors.json file at all.
In fact, I have now deleted that files as I just created a new collection:
eleventyConfig.addCollection("contributors", author => Object.values(author.items[0].data.contributors));
And now instead of calling my authors global data, I simply call collections.contributors in my Nunjucks templates. As always, there's probably a much cleaner way, but I have existing filters to show all guides by a chosen author and their bio on their posts etc.
It's working well, so for now it ships, maybe I'll try to replicate this without creating the extra collection at a later date, if I ever figure it out.

Good data structure for storing collection of items for most effiecient query DynamoDB

I recently ran into a problem where I had to store a collection of data inside an attribute. There is restaurantDB table. The table has an attribute(not required) named "groups" that manages the groups created by the restaurant. These groups have several permissions associated with them. A natural way of thinking is that the groups attribute can be of type list that stores a map(key-value pairs) of the data like group_name, group_id, and permission(that is a list of all permisssions)
table:{
//other fields
id:
groups: L: M: {group_name:S,group_id:S,permission:L :S}
}
e.g.
"groups": [{
"name": "OWNER",
"permission": ["READ", "WRITE"]
},
{
"name": "MANAGER",
"permission": ["READ"],
}
],
This works fine for creation and appending arbitrary number of users using dynamoDB aws-sdk
update with UpdateExpression: 'SET #groups:=list_append(#groups,:newgroup)'
however if i have to do a patch request to modify a permission for a group, say, MANAGER,
How can i retrieve the Map object inside the list with key group_name:"MANAGER" without fetching the whole array of Groups.
I don't want to patch it by fetching the whole list because I will first have to query to get hold of the groups attribute, then I'll have to iterate through whole of the array to findgroup_name:"MANAGER". Once I get hold of it I'll then modify the array then put the whole list back with an update.
The group names are gauranteed to be unique(however the client also wants a unique group_id) so I thought of a data structure something like
table:{
//other fields
id:
groups: M:{S:L}
}
e.g.
"groups":{
"OWNER":["READ","WRITE"],
"MANAGER":["READ"]
}
Though now I cannot enter arbitrary number of data to create a new group(as in POST request) but I can do it one at a time. Also PATCH request now work fine as
UpdateExpression:
"set #groups.#groupName = :newPermission",
ExpressionAttributeNames: {
"#groups": "groups",
"#groupName": `${groupName}`, //groupName taken from request Body
},
ExpressionAttributeValues: {
":newPermission": permission, //permission taken from request body
},
I wanted to know if there was a better way to retrieve list types. The documentation syas we can retrive list items with Indices however I don't know the index in advance.

How to deal with user permissions in single page application

I'm working on a single page enterprise application with a pretty complex logic about user permissions. The huge part of it works entirely on client communicating with backend server using AJAX sending JSON back and forth. The tricky part is that I need to implement permission mechanism as on per-entity basis, and I dont know how to do it the right way.
To explain myself clearly here the example code, I have 2 entity classes on the backend User and Node:
class User {
Long id;
}
class Node {
Long id;
String name;
Status status;
Node parent;
List<User> admins;
}
enum Status {
STATUS_1, STATUS_2
}
I send JSON of parent node to the server:
{id: 1, name: "Node name 1", status: 'STATUS_1'}
And recieve JSON with a bunch of child nodes:
[
{id: 11, name: "Node name 1.1", status: 'STATUS_1'},
{id: 12, name: "Node name 1.2", status: 'STATUS_1'}
]
On the client they are displayed in a tree-like structure, like this:
Now the tricky part:
Simple user that works with application can see tree, but can't change anything.
User can change node name if he is among admins of node or any of its parent nodes.
Admins can also change status of node, from STATUS_1 to STATUS_2, but only if all child nodes has STATUS_2 status.
There is a list of super adminstrators that can do whatever they want: change properties of any node, change status as they want.
So somehow, during rendering of the tree on the client, I need to know what user can or cannot do with each of the node on the page. I can't just assign user a role within a whole application because user rights vary from one node to another. Also I can't see whole picture on the client side because child nodes may be not loaded. How can I manage user permissions in situation like this? What's the proper way or pattern to use?
Should I attach some role object to each node, or maybe a bunch of flags representing what user can or cannot do like that:
{
id: 12,
name: "Node name 1.2",
status: "STATUS_1",
canChangeName: true,
canChangeStatus: false
}
That looks pretty silly to me.
I usually solve complex (and not so complex) permission-based tasks in the application using ACL classes.
I have simple, lighweight classes, that take a model, permissions for which are being checked, and a user object into a constructor. They have a bunch of methods with names canXXXX(). These methods can optionally take some parameters also if that is needed.
If you have the same model classes on front and back, you even might be able to reuse ACLs in both cases.
Can you use this approach?

How to manage large java script data files?

I'm developing a Cordova project. to store my data I'm using java script files like this:
var groups = [
{
id: 1,
parent_id: 0,
name: "Group 1"
},
{
id: 2,
parent_id: 0,
name: "Group 2"
}
];
First problem is that I don't know if it is a good way or maybe there are better ways.
to use this data, simply I use a loop through variable, but the problem is when there are large data volumes, for example thousands of records. It's hard to handle this amount of data in a .js file. what should I do?
A possible solution is to use a database such as IndexedDB(if your app is completely offline) or FireBase (if your app uses internet), you can query and get just the data you require.
Even DOM Storage (Local-Storage) is an option but there is the problem of looping over an array and this cannot store more than 5MB of data.

Get count of unique values of properties from JSON API response

I have a JSON API served by a Ruby on Rails backend. One of the endpoints returns an array of objects structured like this
{
"title_slug": "16-gaijin-games-bittrip-beat-linux-tar-gz",
"platform": "Linux",
"format": ".tar.gz",
"title": "BIT.TRIP BEAT",
"bundle": "Humble Bundle for Android 3",
"unique_games": 9
},
{
"title_slug": "17-gaijin-games-bittrip-beat-linux-deb",
"platform": "Linux",
"format": ".deb",
"title": "BIT.TRIP BEAT",
"bundle": "Humble Bundle for Android 3",
"unique_games": 9
},
Because there are different types of downloads for a single title the "Title" is not unique across several objects. I would like a count of only unique titles.
I was thinking of doing it in Ruby on Rails in the model and just sending it in the JSON response but that does not work because it needs the whole array to count them, obviously. I am using angular on the front end so I am thinking it needs to be done in the controller. I also filter the response in a table and want updated numbers of the unique titles being displayed.
Here's a screenshot of the page this is going on to get better perspective. http://i.imgur.com/Iu1Xajf.png
Thank you very much,
Thomas Le
BTW, this is a site I am developing that is not going to be a public website. It is a database site that holds all the data on the bundles I have bought from IndieGala and HumbleBundle. I am not going to make these links available to the public. I am making it more functional than the bare minimum because it is an open source project that I have on GitHub that people can use themselves locally.
Just in case people were wondering why I have Humble Bundle stuff listed on the image.
http://jsfiddle.net/hy7rasp4/
Aggregate your data in an array indexed by the unique key, Then you get access to information on duplicates and count.
var i,
title,
uniqueResults= {};
for (i in results) {
title= results[i].title;
if (!uniqueResults[title]) {
uniqueResults[title]= [];
}
uniqueResults[title].push(results[i]);
}
Maybe it would be better to restructure your data at the same time, so you can also get those items easily later as well as a quick lookup for the number of titles, e.g. in JavaScript
// assuming arrayOfObjects
var objectOfTitles = {},
i;
for (i = 0; i < arrayOfObjects.length; ++i) {
if (!objectOfTitles.hasOwnProperty(arrayOfObjects[i].title)) {
objectOfTitles[arrayOfObjects[i].title] = [];
}
objectOfTitles[arrayOfObjects[i].title].push(arrayOfObjects[i]);
}
var numberOfTitles = Object.keys(objectOfTitles).length;
// then say you choose a title you want, and you can do
// objectOfTitles[chosenTitle] to get entries with just that title

Categories