Swaping values in array in mongodb - arrays

I read: Swap the values in a MongoDB array
And tried to use that but it didn't work for the example I have so will ask if I missed anything or if I need a different approach.
I have a DB with objects that looks something like this:
{
"_id": ObjectId("5448ec9c97086e180c239346"),
"cfg": {
"children": {
"0": ObjectId("5448ecb2d9f98f3c1746f5ba"),
"1": ObjectId("5448ecb2d9f98f3c1746f5bf"),
"2": ObjectId("5448ecb2d9f98f3c1746f5bb"),
"3": ObjectId("5448ecb2d9f98f3c1746f5c0"),
"4": ObjectId("5448ecb2d9f98f3c1746f5bc"),
"5": ObjectId("5448ecb2d9f98f3c1746f5c1"),
"6": ObjectId("5448ecb2d9f98f3c1746f5bd"),
"7": ObjectId("5448ecb2d9f98f3c1746f5c2"),
"8": ObjectId("5448ecb2d9f98f3c1746f5be"),
"9": ObjectId("5448ecb2d9f98f3c1746f5c3")
}
}
}
It also has the objects listen here in the "children" list.
This is supposed to be a ordered list, so if I want to move index 6 up to index 4, i also have to move 5 down to 6 and 4 down to 5.
The way that I handled this is to try and use the swap suggested in the link I had in the start and then run that recursive.
I don't want to paste to much code here but basiclly what I do is:
function swapChilds(index, i, parent, steps, self,callback){
if (i >= steps + 1)
return callback(null);
// Genereate swap
self.collection.update({_id: parent.meta.parentObjectID},{$set: swap}, function(err){
if (err){
console.log('errSwap:' + err);
return callback(err);
}
swapChilds(index,i + 1, parent, steps, self,callback);
});
}
So this uses index and i to get a correct swaping going and then just calls itself until it has been called steps amout of times (i start with i = 1)
When tying this I was also using console.log(swap) just to see that it looks good, and it does, it looks something like:
{ 'cfg.children.6': 5448ecb2d9f98f3c1746f5c1,
'cfg.children.5': 5448ecb2d9f98f3c1746f5bd }
{ 'cfg.children.5': 5448ecb2d9f98f3c1746f5bc,
'cfg.children.4': 5448ecb2d9f98f3c1746f5c1 }
There are 2 problems, the first is that that last row there should be 'cfg.children.4': 5448ecb2d9f98f3c1746f5bd (notice the last two letters) since this "bd" is the one that should get moved up to position 4. I realize why this happens, it's since I don't get a new version of the parent, so it will use the old list (will fix and test if it makes any difference).
Now despite this error the code does nothing, when I check in the db after running this set, nothing has changed.
I don't understand where the problem is. Am I doing something very wrong here?
Does it have something todo with ObjectID? I've tried to just use the string, i've tried to make new ObjectID(correctValue) but it doesn't do anything at all.
How can I make multiple swaps to keep a list ordered?

I had messed up in the arguments was as simple as that.
If anyone is intressed in the fix the new code looks like:
function swapChilds(index, i, parent, steps, self,callback){
if (i >= steps + 1)
return callback(null);
// Genereate swapdata
self.collection.update({_id: parent._id},{$set: swap}, function(err){
if (err){
console.log('errSwap:' + err);
return callback(err);
}
self.getObjectById(parent._id, function(err,parent2) {
if (err){
console.log('errSwap2:' + err);
return callback(err);
}
swapChilds(index,i + 1, parent2, steps, self,callback);
});
});
}
I've added a call to get the updated parent so that it doesn't use old values after the first swap, I also change to parent._id instead of the wrong one I had before parent.meta.parentObjectId (I just wanted the current object not it's parent).

Related

"expect" to continue test even after failing

I'm still a beginner in Protractor so forgive me if it is not the most optimized code. Although any advice and help are appreciated.
var orderno =["100788743","100788148","100788087","100000000","100786703"];
for (var i = 0; i < orderno.length; i++) {
(function(Loop) {
element(by.css('[autoid="_is_3"]')).sendKeys(orderno[i]);
browser.actions().sendKeys(protractor.Key.ENTER).perform();
expect(element(by.cssContainingText("span.Class", "Store A")).waitReady()).toBeTruthy();
element(by.cssContainingText("span.Class", "Store A")).isDisplayed().then(function(pickfail) {
if (pickfail) {
element(by.css('[class="highlight"]')).getText().then(function(text) {
console.log(text + "-" + "Pass");
});
} else {
console.log("Order Number: Missing");
}
});
element(by.css('[autoid="_is_3"]')).clear();
})([i]);
};
*waitReady is there to wait for the element to come up but I believe it its trying to find it but couldn't so it timesout.
I've created a test where I could input a value in a textbox which would search and check if it exists or not. If it does, it passes but if it doesn't, then fails. But want the loop to continue even if it fails to see if the other remaining items exist.
I think expect would fail when it couldn't find the value thus stopping the whole test. Is there another way to check and continue the whole checking?

Removing data from array correctly

I am facing the problem that I fill up an array with data and when I want to remove it later, even though I call the removeAll() the array still is not empty.
Better see my code for a more clear view on the problem
Updated new code
#objc func textFieldDidChange() {
doSearch()
if let commentText = commentTextField.text , !commentText.isEmpty {
sendButton.setTitleColor(UIColor.blue, for: UIControlState.normal)
sendButton.isEnabled = true
return
}
sendButton.setTitleColor(UIColor.lightGray, for: UIControlState.normal)
sendButton.isEnabled = false
}
func doSearch() {
let caption = commentTextField.text
let words = caption?.components(separatedBy: CharacterSet.whitespacesAndNewlines)
self.usersSuggestion.removeAll()
for var word in words! {
self.usersSuggestion.removeAll()
if word.hasPrefix("#") {
word = word.trimmingCharacters(in: CharacterSet.punctuationCharacters)
self.isCellSelected = true
self.usersSuggestion.removeAll()
API.User.suggestUsers(withText: word, completion: { (user) in
print("closure", word)
self.usersSuggestion.append(user)
self.tableView.reloadData()
print("#", self.usersSuggestion.count)
})
self.usersSuggestion.removeAll()
tableView.reloadData()
} else {
self.isCellSelected = false
self.usersSuggestion.removeAll()
tableView.reloadData()
}
self.usersSuggestion.removeAll()
tableView.reloadData()
}
}
I am facing the problem here that the array, even though I call multiple times the removeAll() method, never gets back to 0. When I have 2 user, link one, the next time I write an # I get the 2 users+ the linked user (so him twice). I can do it infinitely, having like 100 userSuggestions with just 2 existing users.
photos
You are printing the count immediately after inserting an element in the array. This will alway give a count of 1.
// count was zero
self.hashTags.insert(hashTag, at: 0) // adding one
print("# = ", self.hashTags.count) // count is now 1
Given that the API.user... function uses a completion handler, I would assume that this happens in a separate thread or at least asynchronously so you could event get into situations where the UI shows empty and suddenly shows one.
You may want to structure your code differently to better convey the separation between requesting data and displaying the (asynchronously obtained) results. Embedded completion handlers tend be misleading and give the impression that execution will happen in their visually organized sequence.

multiple users not receiving result from Meteor method call - works once, then returns undefined?

Ok this is an interesting one. I am sorting a list of users in Meteor and trying to return the index number of the user to the client. i.e. I want the first user created to be position 1, the second user created to be position 2, third user position 3, etc.
I am using a Meteor method on the server:
Meteor.methods({
setPosition: function(userId) {
let usersArray = Meteor.users.find({}, {sort: {createdAt: 1}}).fetch();
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
let pos = [];
for (i = 0; i < usersArray.length; i ++) {
if (usersArray[i]._id === this.userId) {
pos = i + 1;
}
console.log('this is the position', pos);
return pos;
};
}
}); //Meteor methods
}//end of meteor isServer
The server code above is working perfectly in the terminal EXCEPT the "return pos" line, which makes the code deliver the value to the client for the first user (I see position 1), but break for the subsequent users (res is undefined). If I remove the "return pos" line, then the code works perfectly on the server for ALL users, but I cannot get a single result from my Meteor method call on the client.
This client code below is having trouble receiving the result from the Method call on the server and I don't know why:
Tracker.autorun(() => {
Meteor.subscribe('position');
Meteor.call('setPosition', Meteor.userId(), (err, res) => {
if (err) {
throw new Meteor.Error(err.message);
console.log(err);
} else {
console.log(res);
console.log(Meteor.userId());
Session.set('position', res);
}
});
});
Also, I'm a newb so I apologize for any obvious formatting errors, please point out if you feel the need. Thank you!
Let's just say that this approach is unlikely to scale well as the more users you have the longer it's going to take to find a user's position in the list of users.
Assuming that a user's position never changes (i.e. you don't care about deletions - the 4th user is always 4th even if #3 gets deleted), you'd be better off adding a sequence key to the user object on user creation. You can look at the sequence number of the most recently created user and increment that by one to give it to the next user. You'll want that key to be indexed of course. Then each user can know their sequence number with just Meteor.user().sequence

Protractor check if element is present on a LIST using element.all

I need to do an assertion to check if an element is present inside 'elements.all'.
My first idea is to run a for loop and put an expect inside. Not a great idea because it is checking every single items on list. So if I have 30 items, I might end up with 29 fails.
element.all(by.css(element)).then(function(itemList) {
console.log("Total values in dropdown are: " + itemList.length);
for (i = 0; i < itemList.length; i++) {
itemList[i].getText().then(function(text) {
console.log(text);
expect(text).toEqual('whatever-i-need-to-assert');
});
};
});
In order to solve this problem, I nest an IF statement that will 'pre-check' for a string match. Another bad idea because if there is no match, my expect will never run, thus, giving me a false pass:
element.all(by.css(".item a.gc-exercises-link")).then(function(itemList) {
console.log("Total values in dropdown are: " + itemList.length);
for (i = 0; i < itemList.length; i++) {
itemList[i].getText().then(function(text) {
console.log(text);
if (text == 'BOATLIFT-EXERCISE') {
console.log('Match');
expect(text).toEqual('BOATLIFT-EXERCISE');
} else {
console.log('No Match');
};
});
};
});
Clearly, I am in the wrong path here. Can someone give me an idea how to properly expect for a 'Text' when using element.all. I just need to prove that a text is present on the said list.
Thanks!
Here is an example to check that there is a link with the text "Terms" in a page :
browser.get('https://www.google.co.uk/');
expect(element.all(by.css('a')).getText()).toContain('Terms');
Note that for each element, protractor needs to interogate the browser, which could be slow especially if there is a lot of elements.
A quicker solution would be to check that at least one element is present with a XPath including the expected text:
browser.get('https://www.google.co.uk/');
expect(element.all(by.xpath("//a[text()='Terms']")).count()).toBeGreaterThan(0);
If you just want to check it's present (and other list item's won't interfere), you could call .getText() on the array after element.all, before .then and use toContain()
element.all(by.css(".item a.gc-exercises-link")).getText().then(function(itemList) {
expect(itemList).toContain('some text');
};
Or if you know the index:
element.all(by.css(".item a.gc-exercises-link")).getText().then(function(itemList) {
expect(itemList[3]).toEqual('some text');
}
As a side note: you can use .each() instead of creating a for loop https://angular.github.io/protractor/#/api?view=ElementArrayFinder.prototype.each
You can use filter function.
$$("span").filter(function(elem,index){
return elem.getText().then(function(txt){
return txt === 'text to compare';
});
}).then(function(eleList){ // you get the list of elements containing the text here
console.log(eleList.length);
});

How to execute an array of operations in order with possible breaks in the middle with ReactiveCocoa

Suppose that I have a telephony application. I have a feature that I want to try calling an array of users one by one and break the sequence whenever one of the users accepts call, or when the complete operation is cancelled.
I will try to simplify it like this in pseudocode:
for(user in users) {
result = callUserCommand(user);
if(result == "accepted" || result == "cancelled") {
break;
}
}
Here, the callUserCommand is a RACCommand that needs to be async. And it can actually have three return values: "accepted", "cancelled", "declined".
Accepted and Cancelled will break the sequence of operations and won't execute the rest.
Declined, should continue with the execution of the rest of the sequence.
I tried with something like the following, but really couldn't accomplish exactly the thing I described above.
RACSignal *signal = [RACSignal concat:[users.rac_sequence map:^(User * user) {
return [self.callUserCommand execute:user];
}]];
[signal subscribeNext:^(id x) {
} error:^(NSError *error) {
} completed:^{
}];
If I understood correctly you would like to execute the sequence one by one until one of the call gets accepted or cancelled.
Maybe you could give takeUntil or takeWhile a try. I would write this scenario with RAC like this:
NSArray* users = #[#"decline", #"decline", #"decline", #"accept", #"decline"];
[[[[[users.rac_sequence signal]
flattenMap:^RACStream *(NSString* userAction) {
NSLog(#"Calling user (who will %#):", userAction);
// return async call signal here
return [RACSignal return:userAction];
}]
takeWhileBlock:^BOOL(NSString* resultOfCall) {
return [resultOfCall isEqualToString:#"decline"];
}]
doCompleted:^{
NSLog(#"Terminated");
}]
subscribeNext:^(NSString* userAction) {
NSLog(#"User action: %#", userAction);
}];
In the sample code above the last user who would decline the call won't be called.

Resources