I wrote the code what get Json data and put in listPc. And wrote the loop that add value 'hostName' form listPc to pcBusy List. But my code only add values to second list, If I press GetButton values in pcBusy list duplicates. I need to update the pcBusy List, not only add the same values.
This print if I press button two times:
[S14, S18, S19, S12, S02, V08, S01, O09, S14, S18, S19, S12, S02, V08, S01, O09]
Thanks for help!)
void fetchDataStandart() async {
final urlAuth =
Uri.parse('http://XXX.XX.XXX.XXX/api/usersessions/activeinfo');
final response = await http
.get(urlAuth, headers: <String, String>{'authorization': basicAuth});
if (response.statusCode == 200) {
List listPc = List.from(json.decode(response.body)['result']);
for (int i = 0; i < listPc.length; i++) {
pcBusy.add(listPc[i]['hostName']);
}
print(pcBusy);
} else {
throw Exception('Ошибка получения данных');
}
}
TLDR: add Future<void> as your function return type and consider invoking pcBusy.clear() before overwriting with new data (depends on your logic, though).
With a little more context you'd help me giving you a more complete answer, but here's what I can see from your code:
Your button adds data as many times as you're pressing it. IF you press it two times, you'll get "double" the data, sooner or later. This happens because you use the add method, which just appends data on your list. You can either reset the values with pcBusy.clear() before you add values or do something else if you think that this function shouldn't be overwriting your list. This really depends on your logic;
You're awaiting a Future (via the async keyword), yet your Function doesn't return a Future. This means that - most likely - somewhere else you're awaiting for this function that in reality doesn't need to be awaited. As a consequence this means that when you first press the button, i.e. you fire the future, you can't await for it to happen and your UI doesn't update. The second time, it does update your UI with the previous result and the Future is fired again, letting it update your list with twice the values again as explained in step (1).
Hope this helps. EDIT. Here's some edited code:
// we want this function to be awaited: let it be a Future<void> async function
Future<void> fetchDataStandart() async {
// ... firing an async HTTP request
if (response.statusCode == 200) {
// if everything is OK, decode the JSON
List listPc = List.from(json.decode(response.body)['result']);
// the following will OVERWRITE the list.
pcBusy.clear(); // Or maybe save previous data somewhere else?
for (int i = 0; i < listPc.length; i++) {
pcBusy.add(listPc[i]['hostName']);
}
print(pcBusy);
} else {
throw Exception('Ошибка получения данных');
}
}
To remove duplicates you can convert the list to a set then back to a list again.
pcBusy.toSet().toList();
Related
I have an array of Integers that I need to loop through that then make a network request for further information and with the returned information populate an array of a new object I have created.
I want the returned data to come back in the same order in which the array provides it in, however it is coming back in a different order and am assuming it might have something to do with the network request.
I'm new(ish) to development, so the answer may be very obvious, but I'm really at a dead end with what to do next.
I've tried adding delays to the network request on each loop, i've tried calling .sort() on the array to ensure the array stays in the correct order
var tacticalCoverIdArray = [Int]()
var savedTacticalCoverData = [Covers]()
for coverID in tacticalCoverIdArray {
performGetRequestForSpecificCovers(coverID: coverID, targetURL: targetURL, completion: { (data, HTTPSatusCode, error) -> Void in
if HTTPSatusCode == 200 && error == nil {
do {
if coverID != 0 {
let decodedJSON = try JSONDecoder().decode([Covers].self, from: data)
savedTacticalCoverData.append(decodedJSON[0])
} else {
let data = Covers(id: 0, game: 0, image_id: "")
savedTacticalCoverData.append(data)
}
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000), execute: {
saveTacticalCoverData()
})
} catch let error {
print("Error decoding JSON: \(error)")
}
} else {
print("HTTP status code error: \(HTTPSatusCode)")
print("Error loading data: \(String(describing: error))")
}
})
}
When putting a print statement under the very first declaration of the for-loop (i.e: print(coverID) the return is what I would expect, in which it loops through each integer and then returns them in order.
However, as soon as I put the same print statement under the 'performGetRequestForSpecificCovers' method, the coverID array is not in the order that it should be in, and therefore I get my returned values in an incorrect order when I append them to my 'savedTacticalCoverData' array.
Your hunch about the network requests having an impact on the ordering seems correct.
What I'm guessing is happening here is that when you are looping over tacticalCoverIdArray and calling performGetRequestForSpecificCovers(), that loop doesn't wait for that network request to complete and for the completion block to get called. It continues with the next iteration. Effectively, you are sending tacticalCoverIdArray.count network requests in parallel. Those completion blocks get called much later, long after the outer loop is complete, and most likely even on a different thread.
The most basic, and worst, option is to use DispatchSemaphore to hold up the outer loop until the completion block is called. You'd create a semaphore, call semaphore.signal() inside the completion handler, and call semaphore.wait() at the end of every loop iteration. The problem with this approach is that you will wait for each network request to complete before proceeding to the next one. Also, you will tie up the thread that is executing the first outer loop, and threads are a finite resource, so it's not a good idea to waste them.
A better option is to dispatch all requests at once, and handle the out-of-order responses. This will complete much faster than serially dispatching them, unless you encounter some kind of limitations with dispatching so many network requests in parallel. Instead of savedTacticalCoverData being an array, perhaps it could be a dictionary, where the key is the index of the outer loop, and the value is what you're trying to save? Each time the completion handler gets called, you could check whether or not the dictionary is full and you've accumulated all of the responses you want, and only then proceed with the final "everything is done" action, presumably saveTacticalCoverData().
You'll have to be careful to get your multithreading right. Unless performGetRequestForSpecificCovers() uses only one callback queue, and it's the same queue that this function is running on, you might get called on different threads. If that's the case, I would recommend making a new DispatchQueue and always operating on your dictionary only from that queue, to ensure consistency when those completion blocks come in on random threads. Something like this:
class MyClass {
var tacticalCoverIdArray = [Int]()
var savedTacticalCoverData = [Int: Covers]()
var queue = DispatchQueue(label: "Class Internal Queue")
func myFunc() {
// ... fill in the blanks here
for (index, coverID) in tacticalCoverIdArray.enumerated() {
performGetRequestForSpecificCovers(coverID: coverID, targetURL: targetURL, completion: { (data, HTTPSatusCode, error) -> Void in
if HTTPSatusCode == 200 && error == nil {
do {
queue.async {
if coverID != 0 {
let decodedJSON = try JSONDecoder().decode([Covers].self, from: data)
self.savedTacticalCoverData[index] = decodedJSON[0]
} else {
let data = Covers(id: 0, game: 0, image_id: "")
self.savedTacticalCoverData[index] = data
}
if self.savedTacticalCoverData.count == self.tacticalCoverIdArray.count {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000), execute: {
self.saveTacticalCoverData()
})
}
}
} catch let error {
print("Error decoding JSON: \(error)")
}
} else {
print("HTTP status code error: \(HTTPSatusCode)")
print("Error loading data: \(String(describing: error))")
}
})
}
}
}
I'm trying to make a bot that will delete messages with a certain amount of thumbsdown reactions. I'm having trouble identifying the count of a specific reaction on a message.
Basically, I've created a command that waits for messages and adds them to my msgarray. After each message, I want to go through the array and delete any messages with the specified amount of reactions.
This is what I have so far:
var msgarray = [];
const msgs = await message.channel.awaitMessages(msg => {
msgarray.push(msg);
for (i = 0; i < msgarray.length; i++) {
// I'm not sure where to go from here, I want to make an if statement that checks
// for a certain amount of thumbsdown reactions on the message
if (msgarray[i].reactions) {
// incomplete
}
}
});
This is my first time programming in javascript, so I apologize if this code doesn't make much sense.
TextChannel.awaitMessages() resolves with a Collection, so the msg parameter you're using is not a single message, but a Collection of multiple messages.
Also, it would be better to check messages only when they get a reaction, using the messageReactionAdd event, that fires every time someone adds a reaction.
It should look like this:
// this code is executed every time they add a reaction
client.on('messageReactionAdd', (reaction, user) => {
let limit = 10; // number of thumbsdown reactions you need
if (reaction.emoji.name == '👎' && reaction.count >= limit) reaction.message.delete();
});
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.
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
i've created an array. Each element is a button object. Is there a possibility to hook mouseclick on every array at the same time? I mean something like this.
var Objects:Array = new Array
Objects[0] = new button(parameters)
Objects[1] = new button(parameters)
Objects[2] = new button(parameters)
Objects[n].addEventListener(MouseEvent.CLICK, Clicked(n));
function Clicked(n,...)
{
THECODE PROCEEEEDS for Objects[n]
}
I know that's not the clearest and most correct writing, but I'm asking if this is possible in similiar way? And how to do it? I know I can hook every mouseclick and then check if the clicked under the mouse is one of the array elements with for loop, but I'm asking about this way.
Yes. You are unable to directly pass an index into a listener, but you can retrieve that via calling indexOf() inside it.
for each (b in Objects) b.addEventListener(MouseEvent.CLICK, clicked);
// note, you just put function name here!
public function clicked(e:MouseEent):void {
var i:int=Object.indexOf(e.target);
if (i==-1) {
// panic behavior
return;
}
// now you can parse that index into something valuable
}