Not able to use sort() inside of map function - javascript

The documents in my couchdb look like this:
{
docType: "event",
tags: ["bb","aa","cc"],
...
}
I would like my couchdb view function, to emit a sorted array of tags, so I tried:
function(doc) {
if (doc.docType == 'event') {
if (doc.tags.length > 0) {
emit(doc.tags.sort(), doc._id);
}
}
}
But this (.sort()) is not working as expected: The result shows only documents, where the tags array has only one entity (tags.length = 1).

I found the answer by the help of the couchDB Slack channel. sort() wants to change the positions of the elements in the original array, which is not intended by the view function. You can see exceptions of the javascript engine for each array, to be sorted and which has more than one entries. Need to switch the log level of couchdb to "debug" before.
Using the spread operator to create a new array solved the problem:
emit([...doc.tags].sort(), doc._id);

Related

Why does using slice in MongoDB updateOne does not delete an item from an array but only replaces it with a weird object?

so there the array coursesFollowing
and inside it objects like this for example:
objInside = {
username:"SweetWhite"
courseTitle:"TOMATOaaaaa"
progress:0
}
and I have a function that finds the right object index in the array of objects of that kind of object
and it seems to work in terms of index found and object, however my update request works weirdly when I execute it:
User.updateOne({username: req.body.username}, {coursesFollowing:{$slice: [removeIndex , 1]}}, function(err,result){
if(err){return next(err)}
})
it find the right user to put it in, and also the right field, however it does not remove the object in that removeIndex index, but deletes all the items in the array and puts only one object in, with the array named $slice with the removeIndex as first value and the second value is 1, judging from the data it has not deleted all the other objects but simply replaced them with itself, the operation $slice pushed itself into the array and did not execute I think? I am mildly confused.
anyways, how do I fix this? how do I delete an index of the array without pushing the weird $slice object array thingi?
Thanks!
If you notice your updated document you might realize that you are not updating anything in the array actually using your $slice operator. Instead you are setting a new value for the array. And that is why you see the first element as $slice something and the second as 1.
$slice operator is used with $push and is used to limit the number of elements in the array field finally. The doc doesn't mention removing an array element with this, given its index.
According to this answer there is no simple way to do this.
There is $pull, but it does not work with the given index. It works based on a query condition for the object in the array. So if you are first figuring out the element index and then using it in the update query. You can do that directly.
If you want to use a JS function you can use splice, which does in-place updates. Use the $function
User.updateOne({username: req.body.username}, [{ $set:
{ "coursesFollowing": { $function: { body: function(coursesFollowing) { coursesFollowing.splice(removeIndex, 1); return coursesFollowing; }, args: ["$coursesFollowing"], lang: "js" }} }
}], function(err,result){
if(err){return next(err)}
})

Google AppScript - Merge arrays based on key

I've looked through a lot of similar answers to this question but have not gotten any solutions to work.
I have two separate functions with one array each, that I populate like this:
folderMap.push([{siteurl: filename,logid: fileurl}]);
mapping.push([{siteurl: x[0],shareddriveid: x[1]}]);
I want to have a third function that I want to match the items based on siteurl and put that object in one array while putting any matches that don't have all three items (a siteurl, logid, and shareddriveid) into another. The source arrays will be different sizes. My current code looks like this:
const combined = folderMap.concat(mapping);
const applicable = combined.filter(i => i[0]['siteurl'] && i[0]);
const byTeamUrl = combined.reduce((acc, item) => {
acc[item['siteurl']] = Object.assign(acc[item['siteurl']] || {}, item);
if (item.length > 2) {
Logger.log(item);
}
return acc;
}, {});
Nothing is output, but I know that there should be at least three items that are in both source arrays. What's going wrong here?
Google Apps Script doesn't currently support ECMAScript 2015 (ES6). Hence your arrow functions will not work.
Per Google's docs: Basic JavaScript Features
See this answer: https://stackoverflow.com/a/51628532/8245557
However, you can develop for Apps Script in TypeScript using clasp: Develop Apps Script using TypeScript
I was pushing an array to my object when I should have just been pushing fields. I also reformatted my code so the same object was passed from function 1 to function 2 where function 2 used findIndex to update the value.

How to find the changes between two arrays of objects (src and update), and apply the changes to the src array?

I have two arrays src and update. Both contain objects. Incoming JSON from an API, the objects in the arrays follow the same structure (see below).
I want to compare src against update and add/remove/update items in src that have represented changes in update.
My current process involves iterating both arrays and comparing items using JSON.stringify(), doing multiple passes. Pass one to add, pass two to delete, and pass three to detect changes for replacement (I replace the entire object rather than the fields).
Is there an easier/better way?
Is there a generic utility to help facilitate for this or must I write my own diff detection system?
EDIT: The objects inside the arrays look like (but not limited to):
[
{
id: String,
summary: String,
updates: [
{
date: String,
title: String
},
{ Repeated }
]
},
{ Repeated }
]
As Ali pointed out lodash _.isEqual is a good utility method for this.
Check out https://lodash.com/docs/4.17.4#isEqual
Lodash is really good for helper functions and dealing with data.
If I understand your question correctly both your 'updates' and 'src' array have the same structure - objects with two properties, 'date' and 'title'. You can compare them with nested loops. For example, something like this might be helpful...
updates.forEach(function(u) {
src.forEach(function(s) {
if (s.date === u.date && s.title === u.title) {
// You have a match
}
});
});
I presume you want to identify partial matches, so you can add/replace the if-condition in the inner loop and amend the src array accordingly...
if (s.date === u.date && s.title !== u.title) {
// same date, different title, so...
// change the title of the current
// item from the 'src' array;
s.title = u.title;
}
Or use if-else-if conditions as needed. Regardless, although it's not quite clear from your question which array should be the outer-loop and which should be the inner, the nested 'forEach' loops above will compare each item in the 'updates' array against every item in the 'src' array.
I hope that helped. :)

Why am I unable to sort list by difference of 2 fields?

So I have a list of items.
Each item has a like or dislike button. I wish to sort it by total score, which is = # likes less # dislikes.
I am trying to understand why:
The below handlebar + sort is not working on client side and how to make it work?
If a server side solution would be better and why? (server side takes up unnecessary disk space from what I have learnt in other posts and premature optimization - Yes, Im still in the early stages)
This is how I store it in the list in items.js within collections (ground 0 form).
var item = _.extend(itemAttributes, {
lovers: [],
likes: 0,
haters: [],
dislikes: 0
//popularity: likes - dislikes I tried inserting in collection but doesnt work too
});
So Im sorting it via a handlebar helper + extension of the main list controller
This is the handlebar segment
Template.registerHelper('popularity', function(likes, dislikes) {
var popularity = likes - dislikes;
return popularity;
This is the sorter in router.js
BestItemController = ItemListController.extend({
sort: {popularity: -1},
nextPath: function() {
return Router.routes.BestItem.path({itemsLimit: this.itemsLimit() + this.increment})
}
});
So the handlebar popularity calculations actually does work, the popularity score appears on addition of {{ popularity 123numbersxx }}
However the sorting doesnt work, probably because the sorting does not sort "on the surface" calculations, but rather on the actual item and its fields?
I tried to insert an additional schema field (see above commented line). However that causes errors which states likes are not defined.
Would anyone help guide me a little on this?
Also if you think my method of doing things is bad, appreciate any other ways? For example if sorting on the individual template helper.js files rather than on the main router.js files.
Many thanks!
You can achieve that client-side. Here is how I would proceed:
You probably display your item to be sorted using an {{#each item}} iteration. I would replace item in the #each by a custom helper and create a function, using a cursor or an array as an argument, that will sort your items using the current sorting settings.
Your helper could look like that:
Template.items.helpers({
sortedItems: function(){
return sortMyItems(items.find()) //add .fetch() if you need an array,
//or directly your array if you already have it in a variable.
}
});
And at the beginning of your file, you add the sortMyItems function where you return the sorted list of items.
sortMyItems = function(cursor) {
if(!cursor) {
return [];
}
var sortBy = Session.get("sortBy");// in your case, it would be set to "popularity"
var sortAscending = Session.get("sortAscending ");
if(typeof(sortAscending) == "undefined") sortAscending = true;
var sorted = [];
var raw = cursor.fetch();
// sort
if(sortBy) {
sorted= _.sortBy(raw, sortBy);
// descending?
if(!sortAscending) {
sorted= sorted.reverse();
}
}
return sorted;
}
here I use Session vars, but I advise you to rather use reactive variables or reactive dictionary, since this is a feature related to the current view only.

Updating MongoDB array from variable array in batch operation?

I am working in Node.js and am attempting to push or pull the contents of an array to my mongodb collection. Currently my [working] code to pull objects from the array in FieldArray looks something like this:
for (var i=0; i < MyList.length; i++) {
collection.update(
{field:"MyValue"},
{$pull: {FieldArray: MyList[i]}},
function(err, item){...}
);
}
I'm aware of the ability to use $push/$each, $addToSet/$each and $pullall but they don't seem to accept values dynamically from an array (or I haven't found any indication that it can). Basically, I'd like to be able to use this function with an array of one item or one hundred, using the appropriate batch calls.
Is there any way to make such a call without having to loop through a separate call on the database for each iteration?
You want $pullAll. It does exactly what you are trying to iterate over
collection.update(
{ "field": "MyValue" },
{ "$pullAll": { "FieldArray": MyList } }
)
If that doesn't work then then your array elements are not matching the structure used in your document. Make them that way.

Categories

Resources