My code stays in the second for forever, testing the same category every step and decrementing every time.
I have two arrays, one of them is called categoriesToUpdate and is a list of category ids (string values) for categories that I have to update, and the other is called categories, containing all the actual category data I'm working with.
I have to test if the id value for a category that I have to update is the same as the database and if it is, decrement the attribute position of its object and update the database. But it is infinitely decrementing the bank.
let newCategory;
let name;
let position;
for(let id of categoriesToUpdate) {
for(let cat of categories) {
if(id === cat.id) {
position = cat.category.category.lastPosition - 1;
name = cat.category.category.categoryName;
newCategory = {
category: {
categoryName: name,
lastPosition: position,
}
}
cRef.child(id).update(newCategory);
}
}
}
Examples of the two arrays:
categoriesToUpdate = ['-lGz4j77...', '-uEbKO3...', ...]
and
categories = [
{
category: {
category: {
categoryName: "name",
lastPosition: "number",
}
},
id: "category id";
},
{
...
}
]
it is difficult to explain how I get the arrays, but basically, categoriesToUpdate is an array of ids that I add to my logic, I have to do update in each of these categoriesand categories is an array that has all categories of the database, comes from Firebase.
let id of categoriesToUpdate. I'm assuming categoriesToUpdate is an array of Category objects. So id is actually a Category object. To compare it should be id.id === cat.id.
Also you can try filter instead of a second loop.
Something like
var matched = categories.filter(c => c.id === id.id)[0];
Then compare matched. Nested loops are hard to read, imo.
Related
I'm new in angular and I need some help.
I have an observable getting users of type User[]
User: [
id: string,
name: string
]
and I have another array Ids of type string getting the ids of the selected users from a mat-select
Ids = this.Form.controls['users'].value
what I need right now is to subscribe to users$ observable, and get only the users that they have an id in Ids
const selectedUsers = ids.forEach(id =>this.usersSub$.value.filter((user) => user.userId === id))
something like the above but it is not really the right thing to do because it returns undefined . I'm wondering how should I properly get my selectedUsers array.
You use combineLatest to merge both observables and map all elements to accomplish it.
First, Create an observable with ids.
selectedIds$ = of([1, 3]);
players$ = of([
{ id: 1, name: 'lebron' },
{ id: 2, name: 'irving' },
{ id: 3, name: 'love' },
]);
Next, combine both observables, using the combineLatest operator, and return the players using the map to iterate over the response from the combineLast, use the filter and find to match the playerid with the ids from the selectedIds array.
const seletedUsers$ = combineLatest([this.selectedIds$,
this.players$])
.pipe(
map(([ids, players]) => {
return players.filter((p) => ids.find((id) => id === p.id));
})
)
.subscribe((v) => {
console.log(v);
});
https://rxjs.dev/api/index/function/combineLatest
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
In my state I have an object called foodLog which holds all entries a user enters with one of the keys being foodSelectedKey and I'm trying to return all entries that have a matching value from that key with a different array called foodFilter.
However, this doesn't work and errors out saying foodLog.filter() isn't a function - I've looked this up and it's because it's an Object (I think). Any help would be greatly appreciated!
state = {
// log food is for the logged entries
foodLog: {},
// used for when filtering food entries
foodFilter: [],
};
findMatches = () => {
let foodLog = this.state.foodLog;
let foodFilter = this.state.foodFilter;
let matched = foodLog.filter((item) => {
return foodLog.foodsSelectedKey.map((food) => {
return foodFilter.includes(food);
});
});
};
I guess the reason behind the error Is not a function is that the object can not be looped. By that it means you can not iterate an object with differend variables inside, if it has no index to be iterated like an array. The same goes for map(), find() and similar functions which MUST be run with arrays - not objects.
As far as I understand you have an object named foodLog which has an array named foodsSelectedKey. We need to find intersected elements out of foodFilter with the array. This is what I came up with:
state = {
// log food is for the logged entries
foodLog: {
foodsSelectedKey: [
{ id: 1, name: "chicken" },
{ id: 2, name: "mashroom" }
]
},
// used for when filtering food entries
foodFilter: [
{ id: 1, name: "chicken" },
{ id: 2, name: "orange" }
]
};
findMatches = () => {
let foodLog = this.state.foodLog;
let foodFilter = this.state.foodFilter;
let matched = foodLog.foodsSelectedKey.filter((key) =>
{
for (let i=0; i<foodFilter.length;i++){
if(foodFilter[i].name===key.name)
return true
}
return false;
}
);
return matched;
};
The Output is filtered array, in this case, of one element only:
[{
id: 1
name: "chicken"
}]
In order to check the output - run console.log(findMatches()). Here is the CodeSandbox of the solution. (check console at right bottom)
I have two arrays of users and beams
struct User {
var id: Int
var jobTitle: String
}
struct Beams {
var isSelected = false
var id: Int
var userIds: [Int]
}
If the user selects a particular beam, all the users which are assigned to that beam will be selected of the first array. The user can select multiple beams and i am getting the data from api response, and i have to send the selected users in a post call. so i have to select all the selected users from an array of beams and on the basis of filtered ids, send that array in a post call. initially i am filtering all the beams which are selected like this
beams.filter { $0.isSelected }.compactMap { $0.userIDS }
which gives me an array of [Int]. These are the userIds which have to be sent. I somehow can't figure out how will i select these particular ids from my array of users which contains other attributes as well. contains can be used to filter one element but not a sequence. and even if i filter or take an intersection of these ids and my userArray. i am still left with a set or an array of ids. from which i'd have to generate my array, i want to keep all the other attributes as well. Any help in the right direction would be appreciated. I have something of this nature in my mind
let selectedBeamsIds = beams.filter { $0.isSelected }.compactMap { $0.userIDS }
users.append(selectedBeamsIds.compactMap { getUniqueUsersForBeams(ids: $0) } )
private func getUniqueUsersForBeams(ids: [Int]) -> [User] {
let ower = users.reduce(into: [User]()) { (result,
user) in
let filteredUserIds = ids.filter { $0 == user.id }
//result.append(use)
}
}
I also need to do this with another array of jobTitles which is like
struct JobTitle {
var jobTitle: String
var isSelected = false
}
this one is straight forward as if the users's jobTitle matches with the selected items of [String] array then select that user to be sent.
Try,
let selectedIds = beams.filter { (beams) -> Bool in
beams.isSelected
}.flatMap { (beams) -> [Int] in
beams.userIds
}
let filteredUsers = users.filter { (user) -> Bool in
selectedIds.contains(user.id)
}
1 ) Filter out selected beams
2 ) Collect all the userIds in the selected beams
3 ) Filter users by checking if the id is present in the previously collected ids.
Imagine I have the below setup.
I am trying to sort [first, second, third] alphabetically and also by category.
I'd like featured items first and then everything else sorted by title.
I tried let output = [first, second, third].sort { $0.category == $1.category ? $0.category && $1.category == .featured : $0.title < $1.title }
But it was a complete disaster.
The end result should be that the sorted titles read foo bar baz
enum Category {
case featured, standard
}
struct Article {
var title: String
var category: Category
}
let first = Article(title: "bar", category: .standard)
let second = Article(title: "foo", category: .featured)
let third = Article(title: "boo", category: .standard)
let output = [first, second, third]
More Scalable Solution
When you have nested sorting, you need to group items first and then sort by those groups. For example we can define priority of the categories with an Int value just by constraint the enum to Int:
enum Category: Int {
case featured // Higher level(value) -> Higher priority in sort
case standard // Lower level(value) -> Lower priority in sort
}
Now you should:
Group the data before sort:
let grouped = Dictionary(grouping: input, by: { $0.category })
Sort groups: (So Category cases should have sortable value like an Int)
let sortedGroups = grouped.sorted { $0.key.rawValue < $1.key.rawValue }
Sort each group's data and map it back to the original array:
let result = sortedGroups.map { $0.value.sorted { $0.title < $1.title) } }
This approach is highly scalable and automated. You just need to define new categories in place of their priority. The rest of the code always works as expected.
If you want the Articles to be filtered inside the .featured category as well (in addition to listing featured before the rest):
let res = output.sorted {
if $0.category == $1.category {
return $0.title < $1.title
} else {
return $0.category == .featured
}
}
I have a rather convoluted array structure with 4 nested arrays that I need to manipulate with Mongoose/MongoDB. My code works but it doesn't get the beauty prize.
Right now i have a helper function that will look for an object of said ID and if it doesnt, it will $addToSet and return the array for further manipulation. This feels really clunky and i'm sure some query wizardry will fix this in a simple findOneAndUpdate().
The object structure is as follows:
bookshelf as an Array
Branch Object with an id and an array called Categories
Categories Object with an id and an array called Learnings
Learnings Object with an id and an array called Modules
Modules Object with an id and an array called Cards
It's setup this way due to the collection being one directional in terms of relationships and the documents have no parents. So for the sake of a bookmark functionality, i'm storing the whole path. So a Module could have several cards inside of it.
// TODO can probably do this in some advanced query
addtobookshelf: async function (req, res, next) {
if(!req.body.branch)
return next(boom.badRequest('No Branch id supplied.'));
if(!req.body.category)
return next(boom.badRequest('No Category id supplied.'));
if(!req.body.learning)
return next(boom.badRequest('No Learning id supplied.'));
if(!req.body.module)
return next(boom.badRequest('No Module id supplied.'));
if(!req.body.card)
return next(boom.badRequest('No Card id supplied.'));
try{
const saveslot = await SaveSlot.findOne({'owner':req.decoded._id});
let branchObject = profile.manipulateBookmark(saveslot.bookshelf, req.body.branch);
let categoryObject = profile.manipulateBookmark(branchObject.categories, req.body.category);
let learningObject = profile.manipulateBookmark(categoryObject.learnings, req.body.learning);
let moduleObject = profile.manipulateBookmark(learningObject.modules, req.body.module);
// Finally add the card
moduleObject.cards.addToSet(req.body.card);
await saveslot.save();
return res.status(200).json({message: 'succes', data: saveslot.bookshelf});
} catch(err){
console.log(err);
return next(boom.internal('Woops, something went wrong. Sorry!'));
}
},
manipulateBookmark: function(objectArray, id){
let obj;
objectArray.forEach((element) => {
if(element.id.toString() === id){
obj = element;
return;
}
});
if(!obj){
obj = objectArray.addToSet({id:id})[0];
}
return obj;
},
It's readable so that counts but I'd like to improve my query skills.
[EDIT] Added data structure for clarification
{
"bookshelf": [{
"id": "59034709d44f340011bb8307",
"categories": [{
"id": "58c27aa7734d1d46588667ab",
"learnings": [{
"id": "5881d1f62fd76911007a4ae5",
"modules": [{
"id": "5881d2962fd76911007a4ae8",
"cards": ["5881d5892fd76911007a4ae1"]
}]
}]
}]
}]
}