Related
I've been trying to teach myself Discord bots but I'm a noob so please bear with me. First off I want to say that I really did hunt through this site to find this but everything I came up with seems to only work for numbers, not words ...
I am trying to make a bot give three responses from a list without any chance of repetition. I understand that by preventing an item from repeating, the generated response is no longer "random." This is the workaround I came up with for now:
client.on('message', message => {
const list1 = [
'example A', 'example B'
]
const list2 = [
'example C', 'example D'
]
const list3 = [
'example E', 'example F'
]
if (message.content.startsWith(`${prefix}fmk`)) {
message.channel.send(`**redacted, Marry, Kill:**\n\n${list1[Math.floor(Math.random() * list1.length)]}\n${list2[Math.floor(Math.random() * list2.length)]}\n${list3[Math.floor(Math.random() * list3.length)]}`);
}
});
Obviously you can see the problem with this. It prevents a response like example A, example A, example A, but it also makes it impossible to get a combination like example A, example B, example C.
Is there a way that I could have one list like example A through example F and still get three non-repeating results from it?
I hope this makes sense. Thanks :)
You could use a Fisher-Yates Shuffle to move 3 values around randomly:
function shuffle(array) {
var i = array.length,
j = 0,
temp;
while (i--) {
j = Math.floor(Math.random() * (i+1));
// swap randomly chosen element with current element
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
var ranStrings = shuffle(["Example A", "Example B", "Example C"]); //calling the func
Note: this function not only shuffles the array, but only allowed unique values to be output
EDIT: As mentioned above, this isn't really a Discord.js question, although it is related to a bot, it is more asking about Javascript, and it would be more suited there.
I never got back to this to say that I just ended up using Lodash and it worked great :)
Ok, so recently I've been working on this code, and I was so puzzled and frustrated why it is ordering the output randomly. But then later I realized that it is loading which ever channel loads fastest, then it's index becomes 0, which ever loads the fastest next, becomes 1, whichever loads fastest next becomes 2, etc.
var channels = [
"UCIgnIPif1LQxkdbvBmGNNuA", // channel 1
"UC_Qs0JhHoysG4LItMgGMHHQ", // channel 2
"UC_puGdwVL1kc5VRhaZc6Arw", // channel 3
"UCQvwHZVCrljzvaLVX33Qaaw" // channel 4
];
var key = "cant share but i will tell you the output of the code"
var subCounts = [];
function getSubs(id, key){
for(var i = 0; i < channels.length; i++){
fetch('https://www.googleapis.com/youtube/v3/channels?part=statistics&id='+channels[i]+'&key='+key+'')
.then(response => {
return response.json()
}).then(data => {
//name[i].innerHTML =
subCounts.push(data['items'][0].statistics.subscriberCount);
});
} // for loop close
}
setTimeout(function(){
console.log(subCounts);
// output example: 4, 5, 6, 7
// another output: 5, 7, 6, 4
// another output: 7, 4, 5, 6
}, 1500) // good enough for the fetch to load
it's just random (which ever loads first comes first).
What I want it to do though:
I want it to load based on the order of the channels. So channel 1 needs to load first, then channel 2, then channel 3, etc. Instead of which ever loads first.
Any help, suggestions, tips would be appriciated.
Thank You.
Assuming that it is OK for you to wait for all requests to complete before you begin handling their responses, then Promise.all should allow you to accomplish your goal. Below is your code rewritten roughly to use Promise.all:
var channels = [
"UCIgnIPif1LQxkdbvBmGNNuA", // channel 1
"UC_Qs0JhHoysG4LItMgGMHHQ", // channel 2
"UC_puGdwVL1kc5VRhaZc6Arw", // channel 3
"UCQvwHZVCrljzvaLVX33Qaaw" // channel 4
];
var key = "cant share but i will tell you the output of the code"
var subCounts = [];
function getSubs(id, key) {
var requests = [];
var currentRequest;
for (var i = 0; i < channels.length; i++) {
currentRequest = fetch('https://www.googleapis.com/youtube/v3/channels?part=statistics&id=' + channels[i] + '&key=' + key + '');
requests.push(currentRequest);
} // for loop close
Promise.all(requests).then((responses) => {
for (let j = 0; j < responses.length; j++) {
const currentResponse = responses[j];
const data = currentRequest.json();
subCounts.push(data['items'][0].statistics.subscriberCount);
}
});
}
setTimeout(function () {
console.log(subCounts);
// output example: 4, 5, 6, 7
// another output: 5, 7, 6, 4
// another output: 7, 4, 5, 6
}, 1500) // good enough for the fetch to load
Please note that I generally opted to use modern variable declarations of let/const-- you won't generally want to mix the older var into code using let/const so it would be best to update all of it, or, less desirable, use var for all declarations. Also, this isn't tested (obviously), so it will likely need some rework for your purposes.
If every millisecond counts and you needed to process them in order but as soon as they came in, this would need to be written differently. If you reply as such I can try to put together a POC.
This would be a good use for Promise.all (docs)
Something like:
Promise.all(
channels.map(
channel => fetch('https://www.googleapis.com/youtube/v3/channels?part=statistics&id='+channel+'&key='+key+'')
)
)
.then(Promise.all((responses) => responses.map(response => response.json()))
.then(responses => {
// now responses is an array in order of the results that you can process as you wish
});
An alterantive would be to make subCounts be of the same length as channels, and place the ith channel result at index i in the subCounts array.
You can use async await,
var data = await fetch('https://www.googleapis.com/youtube/v3/channels?part=statistics&id='+channels[i]+'&key='+key+'')
.then(response => {
return response.json()
}).then(data => {
//name[i].innerHTML =
});
subCounts.push(data['items'][0].statistics.subscriberCount);
So to GET an output like this, I had to use some pretty cool tricks (see Analyzing Data from JSON in JavaScript). Please note my data in this example (# rows) is different.
var feeds = [].concat.apply([], dataSet_parsed.map(s => s.data));
//flattens arrays AGAIN to show all targets
var targets = [].concat.apply([], feeds.map(s => s.data));
//return target hits total
var targetList = targets.reduce(
function(prev, cur) {
var val = cur["val"];
prev[val] = ((prev[val] || 0) + 1);
return prev;
}, {});
// Output: {TargetA: 5, TargetB: 6, TargetC: 4, TargetD: 2}
Basically, I'm trying to get a count of how many times a target was seen per group. And fantastic! this works.
Here's my question. how do I display the output---
{TargetA: 5, TargetB: 6, TargetC: 4, TargetD: 2}
---in a table? There are no guarantees that I will always return the same TYPE of Targets (i.e. next load I could have:
{TargetK: 10, TargetL: 2, TargetM: 5, TargetN: 3, TargetO: 7, TargetP: 8}
I've tried using JSON.stringify and .replace() but I'm not getting very far. And even after that, I don't think I could style that output very well.
JSON.stringify(targetList).replace(/\}/g,'').replace(/\{/g,'')
I am currently trying to create a small project to demo Reactive Programming with RxJS. The goal is to show my colleagues that this thing is out there, and it is worth looking into. I'm not experienced with the framework, so that makes things complicated.
I am trying to expand another demo of mine to utilize RxJS.
It is not a very complicated demo, basically I can add any number of small forms, which result in a number calculated by a small formula, and there is a button, which sums up the values of all the forms.
Getting the formula calculate inside the forms is easy, but I figured I could go further.
I want to have the sum operation done automatically through a merged observable.
The only solution I figured out is something along these lines:
//dummy observables
var s1 = Rx.Observable.Interval(100);
var s2 = Rx.Observable.Interval(200);
//Combine the observables
var m = s1.combineLatest(s2, function(x,y){return x+y});
//Subscribe to the combined observable
var sub = m.subscribe(function(x){console.log(x)});
//A new observable is created
var s3 = Rx.Observable.Interval(300);
//Now I need to update all my subscriptions, wich is a pain.
m = m.combine(s3, function(x,y){return x+y});
sub.dispose();
sub=m.subscribe(function(x){console.log(x)});
I figured I could get another observable to notify my subscriptions to update themselves - as knowing how all my subscribers work would render the whole architecture useless, but this sounds like an overkill for a task like this, and I don't just mean the demo, I can't really imagine to have an "every day" real world example where an architecture like this would make things cleaner than just watching for any change, and getting the calculated values "actively" from my forms.
I'd probably do the active getting, and summing of values inside the module handling the forms, and have the "m" observable for the outside world to subscribe to, pushing my values into it from inside the module.
Would this be a correct approach? I'd think yes, because they are owned by my module, I'm supposed to have full control over what is happening to them, but I'm really interested in what more experienced people think about this.
I know I'm late to this party, but I just had this same exact question, and could not find any suitable answer. I figured out how to do this in the context of my application, so unless this is still an open issue for you, I'll post the answer here for posterity.
To set up my problem, I have an Angular form with a number of controls. Anytime the validation state changes on any one of the controls, I need to do something. However, controls can be dynamically added to the form later, and I still need to listen for validation changes on those individual controls as well.
I'll try to alter my approach a little to fit the question's context. Basically, yes the right approach here is to have an outer Observable signal when to have the inner Observables update their subscriptions. This is acceptable architecture and frankly is one of the reasons Reactive Extensions is so powerful. Here's how I did it using RxJS 6.2.2 and TypeScript 3.0.1. Note that at the time this question was asked, or previously answered, this method might not have been available.
import { map, mergeMap, delay } from 'rxjs/operators';
import { interval, Subject, Observable } from 'rxjs';
// utility function to create sample observables
const createObs = (time: number, i: string) => interval(time).pipe(map(val => `${i}: ${val}`))
// create static sample observables.
// these could represent the value changes of known form elements
const obsA = createObs(1000, 'A'); // emit every 1 second
const obsB = createObs(2500, 'B'); // emit every 2.5 seconds
// create a Subject which will represent dynamically adding a new stream of form values to the calculation
const formChanges = new Subject<Observable<string>>();
// this will hold the accumulated results
const results: string[] = [];
// subscribe to the subject
// each time any of the inner observables emits a new value, this will log the latest value of all existing inner observables
formChanges.pipe(
mergeMap(innerObs => innerObs, (outerValue, innerValue, outerIndex, innerIndex) => {
results[outerIndex] = innerValue;
return results;
})
).subscribe(console.log);
// emit the known form value change streams
formChanges.next(obsA);
formChanges.next(obsB);
// this will represent adding some other stream of values to the calculation later
formChanges.next(createObs(1750, 'C').pipe(delay(5000))) // after 5 seconds, emit every 1.75 seconds
// Output
// [ 'A: 0' ]
// [ 'A: 1' ]
// [ 'A: 1', 'B: 0' ]
// [ 'A: 2', 'B: 0' ]
// [ 'A: 3', 'B: 0' ]
// [ 'A: 3', 'B: 1' ]
// [ 'A: 4', 'B: 1' ]
// [ 'A: 5', 'B: 1' ]
// [ 'A: 5', 'B: 1', 'C: 0' ]
// [ 'A: 6', 'B: 1', 'C: 0' ]
// [ 'A: 6', 'B: 2', 'C: 0' ]
// [ 'A: 7', 'B: 2', 'C: 0' ]
// [ 'A: 7', 'B: 2', 'C: 1' ]
// [ 'A: 8', 'B: 2', 'C: 1' ]
I don't think you will find an operator that will directly do what you need.
There is nothing wrong with crafting your own operator though:
var source = //An observable of observables of form data
Observable.prototype.combineLatestObservable = function(resultSelector) {
var source = this;
return Rx.Observable.create(function(obs) {
var disposable = new Rx.SerialDisposable();
var sources= [];
return source.subscribe(
function(x) {
//Update the set of observables
sources.push(x);
//This will dispose of the previous subscription first
//then subscribe to the new set.
disposable.seDisposable(Rx.Observable.combineLatest(sources, resultSelector)
.subscribe(obs));
},
function(e) { obs.onError(e); },
function() { obs.onCompleted(); });
}).share();
}
Or if you wanted to do it with operators:
//Have to use arguments since we don't know how many values we will have
function sums() {
var sum = 0;
for (var i = 0, len = arguments.length; i < len; ++i) {
sum += arguments[i];
}
return sum;
}
source
//Capture the latest set of Observables
.scan([], function(acc, x) {
acc.push(x);
return acc;
})
//Dispose of the previous set and subscribe to the new set
.flatMapLatest(function(arr) {
return Observable.combineLatest(arr, sums);
})
//Don't know how many subscribers you have but probably want to keep from
//recreating this stream for each
.share();
If you look at the source of combineLatest you can see, that it expects a variable number of Observables, not only a single one. With that in mind you could do something like this:
// Whatever Observables you want...
const a = Rx.Observable.of(1,2,3)
const b = Rx.Observable.interval(100).take(5)
const c = Rx.Observable.of(2)
Rx.Observable.combineLatest(a, b, c) // notice, I didn't define a resultSelector
.do(numbers => console.log(numbers)) // you get 3 element arrays
.map(numbers => numbers.reduce((sum, n) => sum + n, 0))
.subscribe(sum => console.log(sum))
Output of this with my example will be:
[3, 0, 2] // array printed in the do block
5 // the sum of the array printed from the subscription
[3, 1, 2]
6
[3, 2, 2]
7
[3, 3, 2]
8
[3, 4, 2]
9
If you would like to call this with an array of Observables you could use Function#apply to do this:
Rx.Observable.combineLatest.apply(Rx.Observable, [a, b, c])
Hope I didn't misunderstood what you are asking
I am using this snippet to merge unknown number of observables -
someArrayOfIdsToPerformRequest: [];
this.isLoading = true;
const mergedRequests: Array<Promise<JSON>> = [];
this.someArrayOfIdsToPerformRequest.forEach((item) => {
mergedRequests.push(this.postsService.remove({id: item.id}) //returns observable
.toPromise());
});
Promise.all(mergedRequests)
.then(() => {
this.isLoading = false;
this.loadPosts();
});
Hope this helps someone!
New to crossfilter. I've a flat data which is given below:
id,name,patientId,conditionId,isPrimary,age,gender,race,Status,CGI
1,M1,1,c1,Y,33,Male,White,Discharged,0
2,M2,1,c1,N,33,Male,White,Discharged,0
3,M3,1,c2,N,33,Male,White,Discharged,0
4,M4,1,c2,N,33,Male,White,Discharged,0
5,M5,1,c3,N,33,Male,White,Discharged,0
6,M6,1,c3,N,33,Male,White,Discharged,0
25,M1,5,c1,Y,33,Male,White,Discharged,1
26,M7,5,c2,N,33,Male,White,Discharged,1
27,M4,5,c4,N,33,Male,White,Discharged,1
28,M4,5,c1,N,33,Male,White,Discharged,1
29,M4,5,c2,N,33,Male,White,Discharged,1
30,M5,5,c4,N,33,Male,White,Discharged,1
29,M2,6,c1,Y,33,Male,White,Discharged,1
30,M2,7,c1,Y,33,Male,White,Discharged,1
I want to do a count on conditionId but since there are multiple records belonging to the same person as identified by patientId, the count of value c1 should be 4 (belonging to patientId 1, 5, 6, 7) - because same patient may have multiple records (for eg. patientId of 1 is repeated 6 times and two of them has c1 which should be counted only once) . I'm struggling to write a group.reduce on conditionId but could not even start.
Thanks in advance.
Here's one way of doing it. In the example I assumed that the first value was the patientId and the second value the conditionId. The code keeps track of grouping keys (concatenation of the patientId and the conditionId) that were seen already and ignores them.
var countMap = [
[1, 'c1'],
[1, 'c1'],
[2, 'c1'],
[2, 'c2']
].reduce(function (r, v) {
var condition = v[1],
groupKey = v[0] + condition;
if (!r.seen[groupKey]) {
r.seen[groupKey] = true;
r.count[condition] = (r.count[condition] || 0) + 1;
}
return r;
}, {seen: {}, count: {}}).count;
countMap.c1; //2
countMap.c2; //1
I do not know about crossfilter or dc.js, that's why I gave you a vanilla JS solution.
It's a little complicated to do this in Crossfilter, but the solution is similar to that provided by #plalx.
Here is a helper function I am using in one of my projects. It's not perfect, and is a bit optimized to reduce dictionary lookups, so it's not the most readable. The basic idea is you need to keep a dictionary of values seen before for each group. You only need to remember patients, because the condition is already known based on the group your are in:
function reduceHelper(accessorFunction) {
var internalCount;
return {
add: function (p, v) {
if(p.unique.has(accessorFunction(v))) {
internalCount = p.unique.get(accessorFunction(v));
p.unique.set(accessorFunction(v), internalCount + 1);
} else {
p.unique.set(accessorFunction(v), 1);
++p.count;
}
return p;
},
remove: function (p, v) {
if(p.unique.has(accessorFunction(v))) {
internalCount = p.unique.get(accessorFunction(v));
if(internalCount == 1) {
p.unique.remove(accessorFunction(v));
--p.count;
} else {
p.unique.set(accessorFunction(v), internalCount - 1);
}
}
return p;
},
init: function () {
return {unique: d3.map(), count: 0};
}
};
}
You'll need to create a Crossfilter (xfilter) on your data and then:
var helperFunctions = reduceHelper(function(d) { return d.patientId; });
var dim = xfilter.dimension(function (d) { return d.conditionId; };
var group = dim.group()
.reduce(helperFunctions.add, helperFunctions.remove, helperFunctions.init);
Your group will now count the number of patients that have each condition. If a condition appears more than once for a given patient, that patient will still only be counted once. At least, it will if my solution works properly :-)