I currently store my data in AngularJS as lists. Such as:
var users = [{id: 1, name: "Mary"}, {id: 2, name: "John"}];
This however leads to for-loops whenever I want to change something.
function gotChangesFromServer(data) {
for (var i=0; i < users.length; ++i) {
if (users[i].id == data.id) {
users[i] = data;
break;
}
}
}
I was looking into underscore.js and found it has some nice functions like values() to get a list of values from a dictionary-like structure. So I could instead do:
var users = { "1": {id: 1, name: "Mary"}, "2": {id: 2, name: "John"} };
function gotChangesFromServer(data) {
users[data.id] = data;
}
function getUsers() {
return _.values(users);
}
But, I realized that AngularJS will call my getUsers() function a lot. On every filter operation or any change to the data.
So it seems I have to choose between two bad solutions. Either for-looping all the time when changing the users, or accessing a user based on id. Or, calling values() often, which I suppose, basically is a for-loop over the properties in the users object.
I expect to have around 1000 - 5000 users in the users list / dictionary.
How would you do it?
You may use underscores findWheremethod to retrieve certain objects:
_.findWhere(users, {id: 123}).data = newDataForThisUser;
Your gotChangesFromServer would then look like this:
function gotChangesFromServer(data) {
_.extend(_.findWhere(users, {id: data.id}), data);
}
Related
This is in the context of a node express route. I receive a get request with a query param that is a list of IDs. Now I need to make a call-out for each ID and store the result of the callout in an array or object. Each element of the first array (containing the IDs) need to be mapped to its corresponding result from the call-out. I don't have a way to modify the endpoint that I'm hitting from this route so I have to make single calls for each ID. I've done some research and so far I have a mixture of code and sudo code like this:
const ids = req.query.ids;
const idMembers = Promise.all(ids.map(async id => {
// here I'd like to create a single object or associative array
[ id: await callout(id); ]
}));
When all promises resolved I need the final result of idMembers to be like: (The response will be an object with nested arrays and objects I've just simplified it for this post but I need to grab that from the res.payload)
{
'211405': { name: 'name1', email: 'email1#test.co' },
'441120': { name: 'name2', email: 'email2#test.co' },
'105020': { name: 'name3', email: 'email4#test.co' }
}
Oh and of course I need to handle the callout and the promise failures and that's when my lack of experience with javascript becomes a real issue. I appreciate your help in advance!!
Some extra thought I'm having is that I'd have to map the results of the resolved promises to their id and then in a separate iteration I can then create my final array/object that maps the ids to the actual payloads that contain the object. This is still not answering any of my questions though. I'm just trying to provide as much information as I've gathered and thought of.
Promise.all returns an array of results (one item per each promise).
Having this temporary structure it is possible to build the needed object.
const arrayOfMembers = Promise.all(ids.map(async id => {
// ...
return { id, value: await callout(id) } // short syntax for { id: id, value: ... } (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer)
}));
// arrayOfMembers = [
// { id: 211405, value: { name: 'name1', email: 'email1#test.co' } },
// ...
// ]
In pure JS it can be done with for loop or .forEach() call to iterate:
const res = {};
arrayOfMembers.forEach(el => {
const { id, value } = el;
res[el] = value;
});
or by using a single reduce() call
const res = arrayOfMembers.reduce((accumulator, el) => {
const { id, value } = el;
return { ...accumulator, [id]: value };
}, {});
in both cases res will be:
// res = {
// '211405': { name: 'name1', email: 'email1#test.co' },
// ...
// }
P.S.
There is a handy library called lodash. It has tons of small methods for data manipulation.
For example, _.fromPairs() can build an object from [[key1, value1], [key2, value2]] pairs.
As you mentioned you have lodash, so I think the following should work:
const arrayOfKeyValuePairs = Promise.all(ids.map(async id => {
// ...
return [ id, await callout(id) ] // array here so it matches what fromPairs needs
}));
const res = _.fromPairs(arrayOfKeyValuePairs);
I'd like to preface this by saying I'm very new to coding (sorry if this is a very straightforward query - but it has me stumped!)
I'm trying to iterate through objects for the first time and delete a specified key-value pair.
I have a function with an array of User objects and within that array I want to delete the person's name from each object.
I've tried iterating through the object using the .length property but get the result Reference Error: name is not defined.
When I try the following it works (but this isn't ideal as I want the code to run without having the specify the individual index number):
delete users[0].name;
delete users[1].name;
for (let i = 0; i < users.length; i++) {
if (users[i] === name) {
users.splice(i, 1);
}
return users;
}
Actual result: ReferenceError: name is not defined
I want the result to remove the name: ' ' value from the object.
You could iterate the array and delete the unwanted propery. This mutates the given data.
for (var i = 0; i < users.length; i++) {
delete users[i].name;
}
An advanced version takes a destructuring with the unwanted property and a rest syntax for getting all other properties and return this object.
users = users.map(({ name, ...object }) => object);
A simple approach would be to loop over the items using forEach and delete the desired property.
let users = [{name: "a", prop1: 1, prop2: 2},{name: "b", prop1: 2, prop2: 2}, {name: "c", prop1: 3, prop2: 2}];
users.forEach(user => {
delete user['name'];
});
console.log("after deleting property", users);
To delete the key-value pair...
var users = [
{name: "John", role: "Cleaner"},
{name: "Jim", role: "Sergent"},
{name: "Jackhn", role: "Baker"},
{name: "Josh", role: "tinker"},
{name: "Jenny", role: "Soldier"}
];
for (var i=0; i < users.length; i++) {
delete users[i].name;
}
There’s only one code block visible in the question, but do you mean delete users[0].name; delete users[1].name; does what you want when there are two users and you’d like to convert that to a loop?
If yes,
for (let user of users) {
delete user.name;
}
That loops over every user in the array and deletes the name property of each one.
I'm trying to take an array like so:
location: [
{Id: "000-000", Name: "Foo"},
{Id: "000-001", Name: "Bar"},
..etc
]
What's the most efficient/cleanest way to pull out the Ids and combine them into a single string while also appending in front of each value a static string ("&myId=")?
More succinctly, what's the most efficient way to turn the above array into the following end-result:
&myId=000-000&myId=000-001
As stated in the title, ES6 is acceptable to use if it offers the best method for accomplishing this.
Use reduce, extracting each Id:
const location2 = [{Id: "000-000", Name: "Foo"}, {Id: "000-001", Name: "Bar"}];
console.log(
location2.reduce((a, { Id }) => `${a}&myId=${Id}`, '')
);
While this is pretty clean and only requires iterating over each item once, in terms of efficiency, for loops are still more performant if you have a huge number of items in the array:
const location2 = [{Id: "000-000", Name: "Foo"}, {Id: "000-001", Name: "Bar"}];
let output = '';
for (let i = 0, { length } = location2; i < length; i++) {
output += '&myId=' + location2[i].Id;
}
console.log(output);
In this particular case, it looks like you’re trying to concatenate URL parameters.
You can iterate over the location array and use the appropriate set of APIs for this: URLSearchParams and URL.
In particular, you’re looking for the append method, which allows mapping multiple value to the same key.
const params = new URLSearchParams(),
locationArray = [
{
Id: "000-000",
Name: "Foo"
},
{
Id: "000-001",
Name: "Bar"
}
];
locationArray.forEach(({ Id }) => params.append("myId", Id));
console.log("Result as a string:", String(params));
console.log(`Explicitly calling \`String\` is usually not needed, since ${params} can just be interpolated, concatenated, or coerced to a String like this.`);
console.log("Result inside a URL:", String(Object.assign(new URL(location), { search: params })));
console.log("Result as a URLSearchParams object (look in the browser console (F12) for better formatting):", params);
But in general, using map and join seems efficient enough.
const staticString = "&myId=",
locationArray = [
{
Id: "000-000",
Name: "Foo"
},
{
Id: "000-001",
Name: "Bar"
}
],
result = locationArray.map(({ Id }) => staticString + Id).join("");
// Or:
// result = staticString + locationArray.map(({ Id }) => Id).join(staticString);
console.log(result);
In the alternative, the first staticString may also be changed to "?myId=", since this looks like query parameters.
But it’s important to use the URLSearchParams API if you’re actually using URL parameters, so that the data is correctly encoded.
Try both approaches with one of the Ids having the value "1&myId=2" and you’ll quickly notice the benefit of the URLSearchParams API.
This API also needs to be used to decode everything again.
I've got a lot of (user) objects which come from a socket. On every new connection I get a new user object, but on every disconnect I just got the ID of the recently disconnected user.
What's the best way to store these user objects client-side?
Already tried something like this (pseudo code without sockets and just for testing)
var connectedUsers = [];
connectedUsers[12] = {
id: 12,
name: "Test",
// ...
};
connectedUsers[29] = {
id: 29,
name: "Test 2"
};
Kinda works - but now I've got an array with lots of empty spaces. The upside is that it would be easy to remove a user just by his ID.
Using an object to store the objects probably won't be the right choice, since I don't have numeric indexes.
Use an object. The former indices are converted to strings and taken as properties for the object.
var connectedUsers = {};
Working example:
var connectedUsers = {};
connectedUsers[12] = { id: 12, name: "Test", };
connectedUsers[29] = { id: 29, name: "Test 2" };
document.write('<pre>' + JSON.stringify(connectedUsers, 0, 4) + '</pre>');
I have an underscore filter which is returning the parent object which contains a child object I am looking for. But I want it to return just the child object. Since it is already doing the work of locating the child object in order to return the parent, I'm wondering how to simplify my code to return just the child. Here's the example:
var filterObj = _.filter(filtersPath, function(obj) {
return _.where(obj.filters, {id: prefilterCat}).length > 0;
});
So here, that nested object inside obj.filters, with the id of prefilterCat, is the object I want returned, not its parent. So currently I would have to do another find inside of filterObject to get what I need. Any ideas?
Underscore's filter method will return the "parent" object but will filter out the ones that don't match the conditional statement. That being the case, if there is only 1 result, then you can just access it similarly to how you would access an array. For instance:
var filterObj = _.filter(filtersPath, function(obj) {
return _.where(obj.filters, {id: prefilterCat}).length > 0;
})[0];
The above example would get the first child that is returned from the filter method.
From your question and code, I'm assuming a data structure like this:
var filtersPath = [
{
filters: [
{id: 0},
{id: 1}
]
},
{
filters: [
{id: 5},
{id: 42}
]
}
];
Now you can get an array of all "parent objects" (which you already have done) that have a filters array containing a object with matching ID:
_.filter(filtersPath, function(obj) {
return _.find(obj.filters, { id: 5 });
});
The advantage of doing it this way is that it will stop searching for a value once it's found one, and not always traverse the entire list.
If you want to actually get an array as result, it's a simple map operation:
_.chain(filtersPath)
.filter(function(obj) {
return _.find(obj.filters, { id: 5 });
})
.map(function(obj) {
return obj.filters;
})
.value();
If you only want to get the first matching object, you don't even need to use a filter or map:
_.find(filtersPath, function(obj) {
return _.find(obj.filters, { id: 5 });
})
.filters;
With lo-dash, this operation will be a little easier:
_.find(filtersPath, { filters: [{ id: 5 }] }).filters