JavaScript/jQuery DTOs/Objects to hold State - javascript

I'm usually a C# developer, but writing a mostly-client side application using jQuery.
It is reasonably straight forward: I have a list of "groups" which have some parameters (like a group name) and a list of "users" who also have some parameters.
At the moment I try to manage that using a <li> with a specific id, so there is a lot of string-matching involved to turn <li id="group-2">Name - ExtraInfo</li> back into something I can work with.
I feel that this approach is stupid because a) I use the UI as back-end for state and b) I rely on string parsing rather than working with real objects.
In C# I would just create a DTO (essentially a List<Group>), but I have not enough experience in JavaScript/jQuery to do that there.
Can someone give me some quick tips how to create a custom object and manage a list of these?

I'd just use arrays and objects (let's say JSON):
var groups = [
{
name: "Name",
info: "Extra"
},
{
name: "Name",
info: "Extra"
}
];
Regarding manipulation of the array. jQuery has some array utilities. For the question in your comment, you'd use grep:
var filteredGroups = groups.grep(function (group, i) {
return group.name == "What I'm interested in";
});
It's the equivalent of a Select().Where() in Linq (I'm not a .NET expert so there may be some things that don't match).
Anyway, that would be just the model part of an MVC structure. You still need some functions to map a collection of groups to a <ul> element. Then you'd have the view too.

Expanding on Ionut G. Stan's answer, I suggest nested objects:
var groups = {
group1: {
name: "Group 1",
users: {
uid1: { name: "User 1" },
uid2: { name: "User 2" },
uid3: { name: "User 3" }
}
},
group2: {
name: "Group 2",
users: {
uid1: { name: "User 1" },
uid4: { name: "User 4" }
}
}
};
Check:
if ("group1" in groups && "uid1" in groups["group1"].users) /* ... */;
Fetch:
var username = groups["group1"].users["uid1"].name
Remove:
delete groups["group1"].users["uid1"];
Add:
groups["group1"].users["uid4"] = { name: "User 4" };
To avoid unnecessary duplication and insert paradoxons:
// all relevant users, for look-up purposes
var allUsers = {
uid1: { name: "User 1" },
uid2: { name: "User 2" },
uid3: { name: "User 3" },
uid4: { name: "User 4" }
}
// groups reference the ID only, details are in allUsers["uid1"] etc
var groups = {
group1: {
name: "Group 1",
users: {uid1: 1, uid2: 1, uid3: 1}
},
group2: {
name: "Group 2",
users: {uid1: 1, uid4: 1 }
}
};
Check:
if ("group1" in groups && "uid1" in groups["group1"].users) /* ... */;
Fetch:
var username = allUsers["uid1"].name
Remove:
delete groups["group1"].users["uid1"];
Add:
groups["group1"].users["uid4"] = 1;

You could use a structure like the following. You would want to flesh it out with more detail, and group probably has a similar structure for an internal array of users.Also some error handling for impossible values (like id = -201) wouldn't hurt.
function Group(id, name, info)
{
var _id = id;
var _name = name;
var _info = info
this.getID = function()
{
return _id;
}
this.getName = function()
{
return _name;
}
this.getInfo = function()
{
return _info;
}
}
function GroupList()
{
var _groups = [];
this.addGroup = function(group)
{
_groups.push(group);
}
this.getGroup = function(id)
{
var group;
for(var index = 0, length = _groups.length; index < length; ++index)
{
group = _groups[index];
if (id === group.getID())
{
return group;
}
}
}
}
If you're going for a OOP style as above, I'd recommend Prototype as it makes class inheritance easier. Then it's just a matter of writing a generic List and extending from that (see http://api.prototypejs.org/language/class.html). Prototype also provides, amongst other things, a pretty broad set of functions for working with arrays and enumerables (see http://api.prototypejs.org/language/enumerable.html).

You should not care about the string matching this is wrapped by jQuery for good reasons!
The fastest way without jQuery would be
document.getElementById('idOfLiElement');

You can also use jQuery's data function to store groups for easy retrieval.
var group1 = {
name: "Group 1",
users: {
uid1: { name: "User 1" },
uid2: { name: "User 2" },
uid3: { name: "User 3" }
}
}
$('#ArbitraryElementID').data('Group 1', group1);
then you can use:
var group1 = $('#ArbitraryElementID').data('Group 1');
to retrieve the object.

Related

How do I save JavaScript variables permenantly/persistently?

I have 5 arrays, which can all be updated by the user. They are structured as:
var nameStore = [
{ name: "Tim Jones", idn: "i0001" },
{ name: "Mark Gooderham", idn: "i0002" }
];
var nameStoreName;
var subjectStore = [
{ name: "Sailing", ids: "s0001" },
{ name: "Navigation", ids: "s0002" }
];
var subjectStoreName;
var classStore = [
{ name: "Class A", idc: "c0001" },
{ name: "Class 2", idc: "c0002" }
];
var classStoreName;
var roomStore = [
{ name: "Room 1", idr: "r0001" },
{ name: "Room 2", idr: "r0002" }
];
var weekStore = [
{ week: 1, weekTimes: ["mon01i0001s0001c0001r0001", "mon02i0001s0002c0002r0002"] },
{ week: 2, weekTimes: ["mon02i0002s0002c0002r0002"] },
];
I want to be able to store these arrays permanently, so when the webpage closes, the arrays will have their data saved, and can then be accessed by another user later. I know this is a big question, but even if you could just direct me to other resources, that would help.
Thanks.
For that purpose, you should store your arrays in a database. Before closing page, submit arrays to DB and update records. When user opens the webpage, query DB and get arrays' contents.
You can use localStorage to store the array on the browser if you're not using database to persist.
localStorage.setItem('key', JSON.stringify(arr));
var item = JSON.parse(localStorage.getItem('key'));

How to add a new object to an array of objects that is nested inside an array of objects in ReactJS?

I am new to ReactJS and JS and always get stuck with updating the state using object spread.
I have simplified my data as follows and consider the 0th index for the state for my example,
state = [
{
id: "1",
name: "Some name",
someNestedArrayProperty: [
{
id: "1",
name: "Name 1"
},
{
id: "2",
name: "Name 2"
}
]
},
{...}
]
I want to add a newObject into someNestedArrayProperty that I receive from action creator, say
newObject = {
id: "3",
name: "Name 3"
}
I am not getting the correct syntax. I tried the following and it is messing up the state.
Below is the simplified code of what I am trying to do in my application,
let someNestedArrayProperty= [
...state[0].someNestedArrayProperty,
action.someNestedArrayProperty
];
return [
{
...state,
[0]: { //I guess the problem is in this line "[0]", but I am not getting the correct syntax
...state[0],
someNestedArrayProperty: someNestedArrayProperty
}
}
];
Note: I am returning the modified state from the Redux reducer
You can use map function to modify your state like following:
const newState = state.map((s, idx) => {
// Modification needed elemet
if (idx === 0) {
return {
...s,
someNestedArrayProperty: someNestedArrayProperty
}
}
// Modification not needed
else {
return s;
}
})

Partial Lenses: Group array of objects by property, use prop value as key

I have an array of objects like this:
[
{ name: "Group 1", value: "Foo" },
{ name: "Group 2", value: "Bar" },
{ name: "Group 1", value: "Baz" }
]
I'd like to use Partial Lenses library to transform these groups to keys of an object with corresponding group's items, like this:
{
"Group 1": [
{ name: "Group 1", value: "Foo" },
{ name: "Group 1", value: "Baz" }
],
"Group 2": [
{ name: "Group 2", value: "Bar" }
]
}
My current approach is like this, assuming I have the source data in a variable called data:
const grouped = L.collect([L.groupBy('name'), L.entries], data)
const setKey = [L.elems, 0]
const getName = [L.elems, 1, 0, 'name']
const correctPairs = L.disperse(setKey, L.collectTotal(getName, grouped), grouped)
L.get(L.inverse(L.keyed), correctPairs)
I don't like that I need to use the grouped and correctPairs variables to hold data, as I probably should be able to do the transformation directly in the composition. Could you help me to compose the same functionality in a more meaningful way?
Here's a Partial Lenses Playground with the above code.
I assume the goal is to actually create an isomorphism through which one can
view such an array as an object of arrays and also perform updates. Like a
bidirectional version of e.g. Ramda's
R.groupBy function.
Indeed, one approach would be to just use Ramda's
R.groupBy to implement a new primitive
isomorphism using L.iso.
Something like this:
const objectBy = keyL => L.iso(
R.cond([[R.is(Array), R.groupBy(L.get(keyL))]]),
R.cond([[R.is(Object), L.collect([L.values, L.elems])]])
)
The conditionals are needed to allow for the possibility that the data is not of
the expected type and to map the result to undefined in case it isn't.
Here is a playground with the above Ramda based
objectBy
implementation.
Using only the current version of Partial Lenses, one way to compose a similar
objectBy combinator would be as follows:
const objectBy = keyL => [
L.groupBy(keyL),
L.array(L.unzipWith1(L.iso(x => [L.get(keyL, x), x], L.get(1)))),
L.inverse(L.keyed)
]
Perhaps the interesting part in the above is the middle part that converts an
array of arrays into an array of key-array pairs (or the other way around).
L.unzipWith1
checks that all the keys within a group match, and if they don't, that group
will be mapped to undefined and filtered out by
L.array. If desired,
it is possible to get stricter behaviour by using
L.arrays.
Here is a playground with the above composed
objectBy
implementation.
You don't need any library, use a generic function that returns a reducer, that way you can use to group any collection with any key. In the example below I used this to group by name, but also by value.
const groupBy = key => (result,current) => {
let item = Object.assign({},current);
// optional
// delete item[key];
if (typeof result[current[key]] == 'undefined'){
result[current[key]] = [item];
}else{
result[current[key]].push(item);
}
return result;
};
const data = [{ name: "Group 1", value: "Foo" },{ name: "Group 2", value: "Bar" },{ name: "Group 1", value: "Baz" }];
const grouped = data.reduce(groupBy('name'),{});
console.log(grouped);
const groupedByValue = data.reduce(groupBy('value'),{});
console.log(groupedByValue);
You can use Array.reduce
let arr = [{ name: "Group 1", value: "Foo" },{ name: "Group 2", value: "Bar" },{ name: "Group 1", value: "Baz" }];
let obj = arr.reduce((a,c) => Object.assign(a, {[c.name]: (a[c.name] || []).concat(c)}), {});
console.log(obj);

How to create json array with the inner array in javascript and access it

I want to create an object with an array property which looks like this:
var arrayOfUsers = {
id: "some user id",
username : "some names",
roles : [array with roles]
}
And i would like to access an element by id, something like, arrayOfUsers['some id']['roles'];
I am new to json. I've tried different ways, but always ended up with bunch of errors.
First, this is a JavaScript object. JSON is a string representation of JavaScript objects.
Second, it's important to know the difference between an object and an array. In general, consider Objects to be defined with curly braces { } and Arrays with braces [ ]
Values in Arrays are accessed by their index with the arr[index] syntax while objects use obj[key] syntax to access the value assigned to some key on the object.
For your scenario, I'd avoid using arrays, because you want to be able to access objects by key, not by index.
var users = {
"some user id": {
username : "some names",
roles : {
"some role id": {
name: "role name"
}
}
}
};
In reality, this isn't a very effective data structure, because you'd likely want to deal with arrays for looping, rendering, etc, but to answer your question about being able to index by the Id of user and role, this is how your data would have to be structured.
Here is how you declare:
var currentUser,
currentRole,
arrayOfUsers = {
id: 1,
username: "Sample Value",
roles: [{
roleId: 1,
name: "Admin"
},
{
roleId: 2,
name: "Cashier"
}]
};
This is how you access it:
for (var i = arrayOfUsers.length; i--;) {
currentUser = arrayOfUsers[i];
for (var x = currentUser.roles.length; x--;) {
currentRole = currentUser.roles[x];
console.log("ID=" + currentRole.id + "Name=" + currentRole.name);
}
}
First, you have to make difference between array which defined by [], and Objects, by {}.
If you want to make an array of JSON, you can do the following :
var arrayRoles = [{
idRole: 1,
type: 'admin'
}, {
idRole: 2,
type: 'user'
}];
var userArray = [{
id: 1,
username: 'toto',
roles: arrayRoles
}, {
id: 2,
username: 'titi',
roles: arrayRoles
}, {
id: 3,
username: 'toto',
roles: arrayRoles
}];
Then, if you want to iterate over all your data, you can do it by using forEach loop, which tends to be more elegant :
userArray.forEach(function(elm){
//Our roles array
var roles = elm.roles;
//For all item in roles array
roles.forEach(function(value){
//display type of role, for example.
console.log(value.type);
});
});
But if you want to search a specific item in your JSON array, you can use filter method, by using high order function.
function filterBy(key, filter){
return function(elm){
return elm[key] === filter;
}
}
Then, you can apply this function to your filter, by passing specific field and value, and it will return an array of results :
var filtered = userArray.filter(filterBy('username', 'toto'));
//Display '1'
console.log(filtered[0].id);
//Display '3'
console.log(filtered[1].id);

how do add to a key value to javascript object

I am trying to learn JS. It seems simple but I am not sure how to do this.
having this javascript object based on this good
thread
var people = {
1: { name: 'Joe' },
2: { name: 'Sam' },
3: { name: 'Eve' }
};
How do I add the following value 4: { name: 'John' }
To get name Eve I write
people["1"].name
Assign the anonymous object to it the way you would any other value.
people["4"] = { name: 'John' };
For what it's worth, since your keys are numeric you could also use zero-based indices and make people an array.
var people = [ { name: 'Joe' },
{ name: 'Sam' },
{ name: 'Eve' } ];
and
alert( people[2].name ); // outputs Eve
people[3] = { name: 'John' };
I think people should be an array :
var people = [
{ name: 'Joe' },
{ name: 'Sam' },
{ name: 'Eve' }
];
as the keys are integers, so you can add a person by :
people.push({name:'John'});
You can acces to the people by doing :
var somebody = people[1]; /// >>> Sam

Categories