I've followed Martin Liversaege's answer from this question on Stack Overflow: What is the implementing class for IGrouping?
I've implemented my own class that derives from IGroupable as such:
public class AjaxResponseGroup<TKey, TElement> : IGrouping<TKey, TElement>
{
readonly List<TElement> elements;
public AjaxResponseGroup(IGrouping<TKey, TElement> ajaxResponseGroup)
{
if (ajaxResponseGroup == null)
{
throw new ArgumentNullException("ajaxResponseGrouping");
}
Key = ajaxResponseGroup.Key;
elements = ajaxResponseGroup.ToList();
}
public TKey Key { get; private set; }
public IEnumerator<TElement> GetEnumerator()
{
return this.elements.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
I've got this fun little LINQ statement in my controller:
var groupedAuthActions = unitOfWork.AuthActions.GetAll()
.WhereIf(q != null, x => x.Name.ToLower().Contains(q.ToLower()))
.OrderByDescending(authAction => authAction.CreatedOn)
.Select(authAction => new AuthActionsListViewModel
{
Id = authAction.Id.ToString(),
Name = authAction.Name,
Description = authAction.Description,
Grouping = authAction.Grouping
})
.GroupBy(authActions => authActions.Grouping)
.Select(g => new AjaxResponseGroup<string, AuthActionsListViewModel>(g))
.ToList();
Which is serialized with the following line:
string serializedObject = JsonConvert.SerializeObject(groupedAuthActions);
However, when that is converted to JSON, the key is not included in the string, instead, the groups are anonymous arrays:
[
[{
"Id": "5fb20278-2f5e-4341-a02d-070d360066cd",
"Name": "CreateAuthAction",
"Description": "Allows the user to create an AuthAction",
"Grouping": "User Management"
}, {
"Id": "1edc56d4-9529-4a18-8684-137d0ccfd4d3",
"Name": "ReadAuthAction",
"Description": "Allows the user to view the AuthActions within the system",
"Grouping": "User Management"
}],
[{
"Id": "f1332b37-44c2-4c86-9cbe-ea3983bf6dfe",
"Name": "DeleteAuthAction",
"Description": "Allows the user to delete an AuthAction",
"Grouping": "Test Group"
}]
]
Other than looking at the Grouping property of the first item in the array, how am I to determine what the key of the group is when using the object in the front-end JavaScript?
Is the only/best solution to do something like: (I'd be doing iterative functions, but to illustrate the idea)
// myObj = JS Object from return JSON above
var firstGroup = myObj[0][0].Grouping;
I feel like there has to be a more elegant and "correct" way of doing things?
Related
I don't understand how to read the genre data.
"data":
"genres": [
{
"id": 0,
"type": "string",
"name": "string",
"url": "string"
},
{
"id": 1,
"type": "string",
"name": "string",
"url": "string"
}
],
}
I want it to end up being displayed like
name1, name2, name3
and (if possible) I want to have each of the names have the link to their corresponding url.
I have tried to find that data by
var genres = data.genres;
But that just returns [object Object],[object Object]
Im guessing that you have to read the data from those objects, but I don't know how to.
What you need to understand from that is the following: "data" is an object and it has a "genres" property which has an array of objects inside it. Knowing this you can just do the following:
const name1 = data.genres[0].name; // Gets the first name
const name2 = data.genres[1].name; // Gets the second name
If you want to loop through the genre data, then you need to iterate through the objects inside the "genres" property:
data.genres.map((genre) => console.log(genre.name)); // Logs all names
This would be a more common example:
for (let i = 0; i < data.genres.length; i++) {
const name = data.genres[i].name;
console.log(name); // Logging the names
}
You'll have to loop to go through the data, and create a-elements to create a link. You'll also need a parent element to append the links to:
const yourParentElement = document.getElementById("parent");
var genres = data.genres;
genres.forEach(genre => {
var lnkGenre = document.createElement("a");
lnkGenre.textContent = genre.name;
lnkGenre.href = genre.url;
You can also add classes or an id here:
lnkGenre.classList.add("yourClass");
lnkGenre.id = genre.id;
yourParentElement.appendChild(lnkGenre);
});
I get response from an api regarding user roles, if user has one role, I get single value place holder i.e
{
"nameid": "1",
"unique_name": "Anees",
"role": "Student",
"nbf": 1587681052,
"exp": 1587767452,
"iat": 1587681052
}
If user has more than one roles, roles consider as array ie.
{
"nameid": "2",
"unique_name": "Naveed",
"role": [
"SuperAdmin",
"Admin",
"Teacher",
"Clerk",
"Student"
],
"nbf": 1587712850,
"exp": 1587799250,
"iat": 1587712850
}
How can I handle both a single value and a collection in same place holder?
This script work fit for me
const userRoles = this.decodedToken.role as Array<string>;
I have to use some collection methods like find etc
var status = userRoles.find(x => x == role);
it gives error in case of single value.
Any solution, please.
You could convert the single item to an array containing single element. Check if the element is an array using Array.isArray() function. Try the following
const userRoles = Array.isArray(this.decodedToken.role) ? this.decodedToken.role : [this.decodedToken.role];
let status = userRoles.find(x => x == role);
As a sidenote, using let instead of var helps to keep things the Typescript way.
Casting will always succeed, so you need to check the type at runtime.
You can check to see if the value is an array using isArray
const isRolesArray = Array.isArray(userRoles)
var status = isRolesArray ? userRoles.find(x => x == role) : userRoles === role;
Or you can cast to the proper typescript type using union types and do a check using type guards. So you can do something like this
const userRoles = this.decodedToken.role as Array<string> | string;
let status
if (typeof userRoles === "string") {
status = userRoles === role
} else {
status = userRoles.find(x => x == role);
}
This way you get all the typechecking goodness from Typescript.
you can use something like this.
Array.isArray() can easily check this
//declares a variable for object
let newobject = {
"nameid": "2",
"unique_name": "Naveed",
"role": [
"SuperAdmin",
"Admin",
"Teacher",
"Clerk",
"Student"
],
"nbf": 1587712850,
"exp": 1587799250,
"iat": 1587712850
}
function t(arr){
let status;
if(Array.isArray(arr)){
status = arr.find(x => x == "Admin");
} else {
status = newobject.role;
}
console.log('status ',status);
}
t(newobject.role); //call this for activating the check
I am new to react native and firebase but understand SQL relationships. I am trying to understand how to model many to many relationships with firebase in NoSQL.
In my scenario, users can own multiple groups and groups can have multiple user owners.
I want to display group information (name, city state) for a particular user. (In this case, doc id: WQQZ6pcMMgQoTxH9cr2XGaPOq9W2).
This is how data is structured in the firebase filestore:
{
"__collections__": {
"Group": {
"group3": {
"city": "Aurora",
"state": "CO",
"name": "Group Three",
"__collections__": {}
},
"group4": {
"state": "CO",
"name": "Group Four",
"city": "Denver",
"__collections__": {}
},
"group5": {
"city": "Aurora",
"state": "CO",
"name": "Group Five",
"__collections__": {}
}
},
"User": {
"Hm56Zhn9TJP9jVYrzJRqHAB8T8H3": {
"last_name": "Sneed",
"first_name": "Sam",
"__collections__": {}
},
"WQQZ6pcMMgQoTxH9cr2XGaPOq9W2": {
"last_name": "Smith",
"first_name": "Will",
"__collections__": {}
}
},
"UserGroups": {
"F4GubhZKqWcJnHahL0TQTOP62Jj1": {
"group3": true,
"group4": true,
"__collections__": {}
},
"WQQZ6pcMMgQoTxH9cr2XGaPOq9W2": {
"group5": true
"__collections__": {}
}
}
}
}
Here is my code:
export default class GroupSelect extends Component {
constructor(props) {
super(props);
this.userGroupRef = firebase.firestore().collection("UserGroups").doc('WQQZ6pcMMgQoTxH9cr2XGaPOq9W2');
this.state = {
userGroups: [
{
uid: 'group1',
name: 'Group One',
city: 'Chicago',
state: 'IL'
},
{
uid: 'group2',
name: 'Group Two',
city: 'Denver',
state: 'CO'
}
]
}
}
componentDidMount() {
this.userGroupRef.get().then((doc) => {
var obj = doc._data;
var group_ids = Object.keys(obj).map(function (key) {
return key;
});
var groups = [];
var group = {};
group_ids.forEach(function (group_id) {
console.log(group_id);
this.groupRef = firebase.firestore().collection('Group').doc(group_id);
this.groupRef.get().then((groupDoc) => {
group.name = groupDoc._data['name'];
group.city = groupDoc._data['city'];
group.state = groupDoc._data['state'];
groups.push(group);
});
});
console.log(groups); //not populated correctly
// this.setState({
// userGroups: groups
// });
});
}
render() {
return (
this.state.userGroups.map((userGroup, index) => (
<Text>{userGroup.name} - {userGroup.city}, {userGroup.state}</Text>
))
)
}
}
If I comment out everything in ComponentDidMount(), the render() shows the original contents of state.UserGroups correctly. But, when I try to populate the array in the ComponentDidMount() and reset the state userGroups var, there is a problem with the join and the timing of the population of the array.
How best to do this?
I am patterning this off of how firebase joins are described here:
https://www.youtube.com/watch?v=Idu9EJPSxiY
Users, eventAttendees and Events many-to-many join in firebase realtime db
But, this uses the realtime database instead of filestore, which is what I want to use.
I want to do the same but with: Users, UserGroups, and Groups
Should the data be structured differently?
I implemented Frank van Puffelen's answer below.
It works but that brings up other questions.
If I use the UserGroups collection as described...
constructor(props) {
super(props);
this.userGroupRef = firebase.firestore().collection("UserGroups").doc(firebase.auth().currentUser.uid);
this.state = {
userGroups: []
}
}
...it works, but see possible "issues" below.
componentDidMount() {
this.userGroupRef.get().then((doc) => {
var obj = doc._data;
//get array of group ids
var group_ids = Object.keys(obj).map(function (key) {
return key;
});
let promises = [];
//issue: making singleton database calls for each group id, maybe not good.
group_ids.forEach(function (group_id) {
promises.push(firebase.firestore().collection('Group').doc(group_id).get())
});
let groups = [];
let parentThis = this; //issue: get ref to parent this to set in Promise.all below, kinda weird, maybe not so clear
Promise.all(promises).then(function(docs) {
docs.forEach((groupDoc) => {
let group = {};
group.name = groupDoc._data['name'];
group.city = groupDoc._data['city'];
group.state = groupDoc._data['state'];
groups.push(group);
});
parentThis.setState({
userGroups: groups
});
});
});
}
If I restructure data and add a groups collection below User...
"User": {
"WQQZ6pcMMgQoTxH9cr2XGaPOq9W2": {
"last_name": "Carter",
"first_name": "Will",
"__collections__": {
"Groups": {
"group1": {
"city": "Chicago",
"state": "IL",
"name": "Group One",
"__collections__": {}
},
"group2": {
"city": "Denver",
"state": "CO",
"name": "Group Two",
"__collections__": {}
}
}
}
}
}
Then, code becomes more straightforward.
constructor(props) {
super(props);
//path: User/WQQZ6pcMMgQoTxH9cr2XGaPOq9W2/Groups
this.userGroupRef = firebase.firestore().collection("User").doc(firebase.auth().currentUser.uid).collection("Groups");
this.state = {
userGroups: []
}
}
componentDidMount() {
this.userGroupRef.get().then((doc) => {
let groups = [];
doc._docs.forEach(function (groupDoc) {
let group = {};
group.name = groupDoc._data['name'];
group.city = groupDoc._data['city'];
group.state = groupDoc._data['state'];
groups.push(group);
});
this.setState({
userGroups: groups
});
});
}
This method seems cleaner to me. But now I have duplicate data in a root Group collection and under User collection.
Is it better to do it in this method with duplicate Group data in the db?
I have a relational db background and learning NoSQL best practices. My instinct is not to duplicate data in the db.
You're printing console.log(groups) outside of the callback where the groups get loaded. That won't work, as data is loaded from Firestore asynchronously, and your log statement runs before any data is loaded.
This is easiest to see if you place a few simple log statements:
console.log("Start loading documents");
group_ids.forEach(function (group_id) {
this.groupRef = firebase.firestore().collection('Group').doc(group_id);
this.groupRef.get().then((groupDoc) => {
console.log("Loaded document");
});
});
console.log("Started loading documents");
When you run this code, it outputs:
Start loading documents
Started loading documents
Loaded document
Loaded document
...
This is probably not the order that you expected, but it perfectly explains why the groups array is empty when you print it: none of the groups has been loaded yet. In fact, if you log groups insode the then() callback, you'll see it getting populated one document at a time.
Any code that needs the documents, needs to either be inside the callback, or wait for the document(s) to be loaded by using a promise. Since you're waiting for multiple documents, use Promise.all():
var promises = [];
group_ids.forEach(function (group_id) {
promises.push(firebase.firestore().collection('Group').doc(group_id).get())
});
Promise.all(promises).then(function(docs) {
docs.forEach((groupDoc) => {
group.name = groupDoc._data['name'];
group.city = groupDoc._data['city'];
group.state = groupDoc._data['state'];
groups.push(group);
});
console.log(groups);
});
This video may answer my question.
... at 13:31: "this would probably be duplicate data that would live both in the top-level user object and in this individual review and we'll talk in future videos about the best strategies to keep these kinds of things consistent."
This leads me to believe that I should duplicate the needed group data under the user and not do the joining bit in the client. https://www.youtube.com/watch?v=v_hR4K4auoQ
Or, maybe not:
From this video: https://www.youtube.com/watch?v=jm66TSlVtcc
... at 7:17 "If you have a sql background, this is very similar to using an intermediate table for joins"
More details here: https://angularfirebase.com/lessons/firestore-nosql-data-modeling-by-example/#Subcollection-Many-to-Many-Relationships
In the structure above, how would you get all the tweets that a user has liked(hearted) using the hearts table that has the foreign keys with a single query?
This question already has an answer here:
Recursively list all object property paths from JSON [duplicate]
(1 answer)
Closed 6 years ago.
need some help.
I have a json object and I want to create an string array with keys of the object.
json Object
{
"FEATURES": {
"APP_DASHBOARD": {
"MENU_TITLE": "Dashboard",
"HEAD_TITLE": "Dashboard",
"HEAD_DESC": "Welcome to briteplan"
},
"APP_TEAM": {
"MENU_TITLE": "Teams",
"HEAD_TITLE": "Teams",
"HEAD_DESC": "",
"TOOL_TIPS": {
"TEAM_REFRESH": "Refresh teams",
"TEAM_ADD": "Add new team",
"TEAM_EDIT": "Edit team",
"TEAM_REMOVE": "Remove team",
"MEMBER_REMOVE" : "Remove team member",
"MEMBER_LEAD" : "Team lead",
"AVL_MEMBERS_REFRESH" : "Refresh available members"
},
"CONTENT": {
"TEAMS_TITLE": "Teams",
"MEMBERS_TITLE": "Members",
"AVL_MEMBERS_TITLE": "Available team members"
}
}
},
"GENERAL": {
"SEARCH_PLACEHOLDER": "Search ..."
}
}
I would like to generate a array that looks like this:
var myArray = [
'FEATURES.APP_TEAM.MENU_TITLE',
'FEATURES.APP_TEAM.HEAD_TITLE',
'FEATURES.APP_TEAM.HEAD_DESC',
'FEATURES.APP_TEAM.TOOL_TIPS.TEAM_REFRESH',
'FEATURES.APP_TEAM.TOOL_TIPS.TEAM_ADD',
'FEATURES.APP_TEAM.TOOL_TIPS.TEAM_EDIT',
'FEATURES.APP_TEAM.TOOL_TIPS.TEAM_REMOVE',
];
Not all values is included, but I think you get the Idea. The main thing is I Will know the I have "FEATURES" in the object and I will know the feature name "APP_TEAM", but I don't know the nesting level within that feature.
Hope anyone can help me.
recursion:
function getKeys (o) {
var keys = [];
for (var prop in o) {
if(o.hasOwnProperty(prop)) {
if(typeof o[prop] === 'object') {
getKeys(o[prop]).forEach(function (nestedProp) {
keys.push(prop + '.' + nestedProp);
});
}
else {
keys.push(prop);
}
}
}
return keys;
}
on the above object, returns:
[
"FEATURES.APP_DASHBOARD.MENU_TITLE",
"FEATURES.APP_DASHBOARD.HEAD_TITLE",
"FEATURES.APP_DASHBOARD.HEAD_DESC",
"FEATURES.APP_TEAM.MENU_TITLE",
"FEATURES.APP_TEAM.HEAD_TITLE",
"FEATURES.APP_TEAM.HEAD_DESC",
"FEATURES.APP_TEAM.TOOL_TIPS.TEAM_REFRESH",
"FEATURES.APP_TEAM.TOOL_TIPS.TEAM_ADD",
"FEATURES.APP_TEAM.TOOL_TIPS.TEAM_EDIT",
"FEATURES.APP_TEAM.TOOL_TIPS.TEAM_REMOVE",
"FEATURES.APP_TEAM.TOOL_TIPS.MEMBER_REMOVE",
"FEATURES.APP_TEAM.TOOL_TIPS.MEMBER_LEAD",
"FEATURES.APP_TEAM.TOOL_TIPS.AVL_MEMBERS_REFRESH",
"FEATURES.APP_TEAM.CONTENT.TEAMS_TITLE",
"FEATURES.APP_TEAM.CONTENT.MEMBERS_TITLE",
"FEATURES.APP_TEAM.CONTENT.AVL_MEMBERS_TITLE",
"GENERAL.SEARCH_PLACEHOLDER"
]
I'm trying to create a search pipe for Angular 2.
It should take a nested object and filter out the objects matching a search term.
I have a basic version working but I have run into two problems.
Problem one is, that I now have to hard code key names or use what seems to me like a dirty hack with JSON.stringify.
What is a more elegant way to filter the object, matching the term with any value exept a list of hardcoded ones like _id and url?
Problem two is, that if the string term contains a space, I would like to split it and filter the obj with both terms.
I can split it using terms = term.split(' ');.
But how do I achive filtering with multiple terms?
My current code:
import {Pipe} from 'angular2/core';
#Pipe({
name: "search"
})
export class SearchPipe{
transform(obj: any, [term]) {
if (obj != null && term) {
return obj.filter( el => {
//var test = JSON.stringify(el);
//return test.toLowerCase().includes(term.toLowerCase()); //trows a compile error but seems to work.
return el.name.toLowerCase().includes(term.toLowerCase()) || el.place.toLowerCase().includes(term.toLowerCase()) || el.title.toLowerCase().includes(term.toLowerCase()) ;
});
} else {
return obj;
}
}
}
Expected input format:
[
{
"_id": "56ffbe512ba199777d51c6ae",
"picture": "http://placehold.it/36x36",
"name": "Melissa Reeves",
"company": "Orbixtar",
"place": "Greenwich, Belarus",
"title": "voluptate est ipsum",
"location": {
"lat": -78.926961,
"lng": 90.157653
},
"url": "http://lol.lol"
},
{
"_id": "56ffbe5119cf66e94b3800b4",
"picture": "http://placehold.it/36x36",
"name": "Chelsea Lindsay",
"company": "Squish",
"place": "Vowinckel, Belarus",
"title": "anim ea exercitation",
"location": {
"lat": 66.547582,
"lng": 162.720388
},
"url": "http://lol.lol"
}
]
If the term is "term1" I would like it to return objects that contains "term1".
If on the otherhand term is "term1 term2 term3" I would like for it to return all objects that contain all terms but not necessarily in that specific order.
As an exaple.
The term "Melissa" should return the first object.
The term "Melissa Belarus" should also return only the first object.
Even though the second object matches one of the keys.
Try this (untested)
#Pipe({
name: "search"
})
export class SearchPipe{
transform(obj: any, [term]) {
if (obj != null && term) {
return obj.filter( el => {
var test = JSON.parse(JSON.stringify(el));
delete test['url'];
delete test['_id'];
var testString = JSON.stringify(test);
Object.keys(test).forEach(k => {
testString = testString.replace(k, '');
});
let terms = term.replace(/[\s]+/gm, " ").replace(/^[\s]|[\s]$/gm, "").split(' ');
let containCount = 0;
terms.forEach(t => {
if(testString.toLowerCase().indexOf(t.toLowerCase()) > -1)
{
++containCount;
}
});
return (containCount == terms.length);
});
} else {
return obj;
}
}
}