I am building out an Angular2 Slider component and the current setup is that the value of the slider is a percentage (based off where the slider handle is from 0% - 100%). I have an array of n items and want the slider to grab the appropriate index from the array based off the percentage (where the handle is at).
Here is my current drag event (fired when user is dragging slider handle):
handleDrag(evt, ui) {
let maxWidth = $('#slideBar').width() - 15;
let position = $('#slideHandle').css('left');
position = position.replace('px', '');
let percent = (+position / +maxWidth) * 100;
this.year = percent;
}
The percentage is working correctly but am wondering how I should structure the algorithem to fetch the array index by percentage. So, if i'm at 50%, I want to fetch the array index 73 if the array length is 146.
Is there an easier way of doing this with JavaScript? I have done a similar component where I did a table approach but would like to figure out a way to do this without adding 'helper html elements' to the page.
Your approach sounds fine, so getting the index would could be achieved by using the .length property of your array as follows:
var actualndex = Math.floor((array.length-1) * percentage);
// Where percentage is a value between 0 and 1
This should return an index between 0 and array.length-1 depending on the percentage value.
TL;DR
This answer didn't work for me. I did some experimenting and found that Math.round is better than Math.floor in this situation, but using Math.floor with some additional logic is even better still. For best results, try:
var index = Math.min(Math.floor(array.length * percentage), (array.length-1));
Intro
I know this question seems to be answered, but I'm posting another answer because using the selected answer gave me unexpected results, and I want to save others from running into the same problem.
First of all, I want to clarify that if you only need an approximation, not a precise result, the selected answer will mostly work, especially for a larger array.
However, for smaller arrays, it frequently does not return the expected result. For example, with an array size of 2, it always returns the first element (index 0) unless the percentage is exactly 100%. Even 99% will return the 1st element (Math.floor((2-1) * 0.99) == 0). In fact, 100% is the ONLY value that will yield the last index (no matter the size of the array).
Expected Results
By "expected" results, I mean, that if an array has N elements, each element is represented by 1/Nth of the values from 0.0 to 1.0. An array of 5 elements would be break down like this:
Given Percentage
Expected Index
[0.00%, 20.00%)
0
[20.00%, 40.00%)
1
[40.00%, 60.00%)
2
[60.00%, 80.00%)
3
[80.00%, 100.00%]
4
Algorithms
I looked at 3 different algorithms with various array sizes:
var indexFloor = Math.floor((array.length-1) * percentage);
var indexRound = Math.round((array.length-1) * percentage);
var indexCustom = Math.min(Math.floor(array.length * percentage), (array.length-1));
Results
All 3 algorithms tend to the same results as the array gets larger, but the 3rd one always gives the results that I would expect.
Take a look at the results from arrays of sizes 2 and 5 (incorrect results in BOLD) (Prettier version):
Percentage
Floor(Size 2)
Round(Size 2)
Custom(Size 2)
Floor(Size 5)
Round(Size 5)
Custom(Size 5)
0.00%
0
0
0
0
0
0
5.00%
0
0
0
0
0
0
10.00%
0
0
0
0
0
0
15.00%
0
0
0
0
1
0
20.00%
0
0
0
0
1
1
25.00%
0
0
0
1
1
1
30.00%
0
0
0
1
1
1
35.00%
0
0
0
1
1
1
40.00%
0
0
0
1
2
2
45.00%
0
0
0
1
2
2
50.00%
0
1
1
2
2
2
55.00%
0
1
1
2
2
2
60.00%
0
1
1
2
2
3
65.00%
0
1
1
2
3
3
70.00%
0
1
1
2
3
3
75.00%
0
1
1
3
3
3
80.00%
0
1
1
3
3
4
85.00%
0
1
1
3
3
4
90.00%
0
1
1
3
4
4
95.00%
0
1
1
3
4
4
100.00%
1
1
1
4
4
4
For size 2, both Round and the custom algorithm produce the expected results. However, for size 5, only the custom algorithm does. Specifically, the Round method has 85% selecting the 3rd element (I would expect 80% and over to be the last element) and 15% selecting 2nd element (I would expect that to require 20%).
We see similar results with slightly larger table sizes (Incorrect Results in bold) (Prettier Version):
Percentage
Floor(Size 8)
Round(Size 8)
Custom(Size 8)
Floor(Size 10)
Round(Size 10)
Custom(Size 10)
0.00%
0
0
0
0
0
0
5.00%
0
0
0
0
0
0
10.00%
0
1
0
0
1
1
15.00%
1
1
1
1
1
1
20.00%
1
1
1
1
2
2
25.00%
1
2
2
2
2
2
30.00%
2
2
2
2
3
3
35.00%
2
2
2
3
3
3
40.00%
2
3
3
3
4
4
45.00%
3
3
3
4
4
4
50.00%
3
4
4
4
5
5
55.00%
3
4
4
4
5
5
60.00%
4
4
4
5
5
6
65.00%
4
5
5
5
6
6
70.00%
4
5
5
6
6
7
75.00%
5
5
6
6
7
7
80.00%
5
6
6
7
7
8
85.00%
5
6
6
7
8
8
90.00%
6
6
7
8
8
9
95.00%
6
7
7
8
9
9
100.00%
7
7
7
9
9
9
The Round and Custom methods are very similar (especially for the size 10 array). They are, in fact, identical up until 60%, which the Round method yields index 5, but the custom method correctly classifies that as index 6. From then on, the Round algorithm is off by one.
Conclusion
The original algorithm's use of Math.floor was correct, but the method used to ensure the result was a valid array index skewed the results because there are there are N elements, not N-1 elements. But when we use N, a percentage value of 100% yields a value that is outside of the valid range of 0 to N-1.
However, by definition, 100% should return the last index of the array, and this can be enforced with a simple call to Math.min. The result is this algorithm for determining an array index using a percentage:
var index = Math.min(Math.floor(array.length * percentage), (array.length-1));
Backup
Complete table of all the tested array sizes and percentages
Excel document I made for testing
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 months ago.
Improve this question
Is their a short formula for calculating the bit value for a given row and column?
Example: getTTBit(row = 3, col = 2) = 1
4 2 1
0 0 0
0 0 1
0 1 0
0 1 1 <-- row 3, col 2 -> 1
1 0 0
1 0 1
1 1 0
1 1 1
It should be as quick as possible. I dont want to convert the row number into a bitarray and get the col'th element because I work with large numbers (rows). My bad current solution looks like this (TS):
export function getTTBit(numCols: number, row: number, col: number): bit {
return getNumberAsBitArray(row, numCols)[col];
}
export function getNumberAsBitArray(n: number, length: number): bit[] {
return [...Array(length)].map((_, i) => n >> i & 1).reverse() as bit[];
}
basically, you're asking how to check if the Nth bit is set in a number?
This is the same in all three languages:
isSet = (row & (1 << col)) != 0
where col is counted from the right (lsb=0).
I'm making a custom function in google sheets which counts the number of occurrences of all items in a given array in a given range of cells.
The way I'm given to understand google sheets functions work are that the range you give is turned into a two-dimensional array of the items in the cells. So range A4:B5 would be transmitted to the function as
[[the contents of A4, the contents of B4],
[the contents of A5, the contents of B5]
The next input is a list of the items to check for in those cells. From what I could find online, arrays are given in google sheets by using brackets like these {}. the function I created is given below. I have never used javascript before but I know other languages and I just googled how to use for loops and if statements to create the function, so I'm certain the error is due to something simple that I don't know about or missed.
function count_if_in_set(range, given_list) {
let counter = 0;
for (dim_1 of range) {
for (dim_2 of dim_1) {
for (item of given_list) {
if (item == dim_2) {
counter += 1
}
}
}
}
return counter
}
When I try to use this function in google sheets with the following input: =count_if_in_set(Z30:Z33, {1}), I receive the following error: TypeError: given_list is not iterable (line 5).
The contents of cells Z30 to Z33 are the integers 1, 2, 3, 3 which should be given to the function as the following 2-dimensional array: [[1], [2], [3], [3]]
The problem is that the list [1] is not iterable. I have 2 hypotheses as to why this is:
I coded something wrong because I'm very new to Javascript
The input {1} is not transmitted to a list when google sheets gives it to the function
To check if it was the former, I went through all the aspects of my function. I first checked if you have to declare the type of variable it was when you created the function, but according to what I saw when I googled it you don't. I then changed all my for (a of b) to for (let a of b) but that did nothing to help, and after that I was stuck.
To try and solve it in the case it was a problem with giving the code an array, I tried changing my input from =count_if_in_set(Z30:Z33, {1}) to =count_if_in_set(Z30:Z33, [1]), but that threw up a formula parse error so I knew that wasn't it, and I tried changing the input to =count_if_in_set(Z30:Z33, (1)) but that returned the same error. And after that I was stuck and had no more ideas.
You can get the same result with a plain vanilla spreadsheet formula, like this:
=arrayformula( countif(Z30:Z43, { 1, 2, 3 }) )
To get just the grand total, use this:
=arrayformula( sum( countif(Z30:Z43, { 1, 2, 3 }) ) )
To count how many cells have a text string that contains one of the search keys, use this:
=arrayformula( sum( countif( Z30:Z43, "*" & { "a", "b" } & "*" ) ) )
If you need to use a custom function for some reason, try something this to get started:
function count_if_in_set(values, given_list) {
let counter = 0;
values.map(row => row.map(value =>
counter += (given_list.indexOf(value) !== -1)
));
return counter;
}
This is really an anti-pattern, because the map result is not used for anything. People would tend to use Array.reduce(), but the map-map pattern may be easier to follow, and it is the one typically employed in custom functions that most often do not aggregate the result but return exactly one value per argument value.
Some of the best resources for learning Google Apps Script include the Beginner's Guide, the New Apps Script Editor guide, the Fundamentals of Apps Script with Google Sheets codelab, the Extending Google Sheets page, javascript.info, Mozilla Developer Network and Apps Script at Stack Overflow.
Try this:
Just looking for the number of 1,2,3,4,5,6,7,8 or 9 in the selected range and return the item and count
function checkforitems(a, b) {
let obj = {pA:[]};
Logger.log(a);
Logger.log(b);
let arr = b[0];//b enters as a 2d array with a single element
//collect counts with a pivot table
a.forEach(r => {
r.forEach(c => {
let index = arr.indexOf(c);
if(~index) {
if(!obj.hasOwnProperty(arr[index])) {
obj[arr[index]]=1;
obj.pA.push(arr[index]);//collect elements as an array
} else {
obj[arr[index]]+=1;
}
}
});
});
let l = '';
//obj.pA.sort((x,y) => x - y);//if searching for numbers you can use this to sort them before displaying them
obj.pA.forEach(e => {
l += `${e}-${obj[e]}\n`;
});
return l;
}
My Test Sheet:
COL1
COL2
COL3
COL4
COL5
COL6
COL7
COL8
COL9
COL10
1
17
8
10
2
7
4
19
12
11
8
13
7
1
6
14
8
19
15
1
17
15
15
6
7
3
3
17
8
12
8
2
17
9
9
7
15
16
19
11
14
11
19
0
15
4
16
11
1
11
1
3
3
19
3
1
5
4
3
16
10
8
8
2
17
18
0
1
17
6
1
0
10
18
12
16
11
4
7
13
10
18
6
12
12
5
3
11
9
5
13
2
2
8
5
4
8
12
18
2
0
18
18
18
17
4
6
14
8
8
1
11
12
1
15
17
18
3
0
6
19
5
17
11
12
9
12
1
6
15
12
5
7
1
14
9
4
4
18
12
3
1
11
8
11
9
17
6
12
5
11
12
16
5
5
5
6
12
3
5
16
0
18
14
8
4
16
0
10
0
15
13
4
17
14
10
9
9
2
4
13
12
11
15
12
18
0
8
19
19
3
1
0
3
1
16
18
6
1
2
My formula (L22):
=checkforitems(A2:J21,{1,2,3,4,5,6,7,8,9})
returned result:
1-16
8-14
2-8
7-6
4-11
6-10
3-12
9-8
5-11
Test Sheet With Results:
To anyone looking for a way to make that function work:
I did the same thing but changed it slightly so the second input was a range of cells which contained the range I wanted to search through
What I'm trying to do
I have a Javascript client (in browser) that sends 10 1mb arrays of data (this would normally be binary data from files but I tried to simplify as much as possible) to a websocket server
The client sends all of the data as fast as it can in a normal for loop
The server reads from the websocket connection, reading the data into memory and then does some processing on that data before moving on (in the go example, time.Sleep is used to represent "processing data", the Python version is already quite slow so I didn't add a sleep)
Code to reproduce
Client
const ws = new WebSocket('ws://127.0.0.1:8080/');
ws.onopen = async () => {
for (let i = 0; i < 10; i++) {
// Create a 1mb array filled with the value of `i`.
const data = new Uint8Array(1024 * 1024);
for (let j = 0; j < 1024 * 1024; j++) {
data[j] = i;
}
ws.send(data);
}
};
Servers (either one will reproduce issue)
Python:
from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket
class SimpleEcho(WebSocket):
def handleMessage(self):
print(self.data[0:10])
def handleConnected(self):
print(self.address, 'connected')
def handleClose(self):
print(self.address, 'closed')
server = SimpleWebSocketServer('127.0.0.1', 8080, SimpleEcho)
server.serveforever()
Golang:
package main
import (
"fmt"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"net/http"
"time"
)
func websocketHandler(w http.ResponseWriter, r *http.Request) {
upgrader := websocket.Upgrader{}
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
ws, _ := upgrader.Upgrade(w, r, nil)
defer ws.Close()
for i := 0; i < 10; i++ {
_, fileBytes, _ := ws.ReadMessage()
fmt.Println(fileBytes[0:10])
// Represents taking 1 second to process uploaded data.
time.Sleep(time.Second * 1)
}
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/", websocketHandler).Methods("GET")
_ = http.ListenAndServe("127.0.0.1:8080", r)
}
Desired Behavior
All of the data from the javascript client should end up on the go server, with the
server printing out a received array for each number, here's an example server log which
shows the first 10 bytes of each message received:
[0 0 0 0 0 0 0 0 0 0]
[1 1 1 1 1 1 1 1 1 1]
[2 2 2 2 2 2 2 2 2 2]
[3 3 3 3 3 3 3 3 3 3]
[4 4 4 4 4 4 4 4 4 4]
[5 5 5 5 5 5 5 5 5 5]
[6 6 6 6 6 6 6 6 6 6]
[7 7 7 7 7 7 7 7 7 7]
[8 8 8 8 8 8 8 8 8 8]
[9 9 9 9 9 9 9 9 9 9]
This is exactly what I want, each different array was received only once.
What actually happens
is that some of the data is read by the server, and then the server reads the same data
again from the websocket, instead of new data.
In the below server log, you can see that it works fine until the data array containing
"5"s gets received 4 times in a row, and then jumps to receiving the "8"'s:
Serving at http://127.0.0.1:8080
[0 0 0 0 0 0 0 0 0 0]
[1 1 1 1 1 1 1 1 1 1]
[2 2 2 2 2 2 2 2 2 2]
[3 3 3 3 3 3 3 3 3 3]
[4 4 4 4 4 4 4 4 4 4]
[5 5 5 5 5 5 5 5 5 5]
[5 5 5 5 5 5 5 5 5 5]
[5 5 5 5 5 5 5 5 5 5]
[5 5 5 5 5 5 5 5 5 5]
[8 8 8 8 8 8 8 8 8 8]
This only happens if the Javascript is run in Firefox (v82.0 64-bit), the code works as
intended if I run it in Chrome.
I have also tested in Firefox Developer edition (83.0b4) and the same bug happens.
Weird things that stop the issue
If the time.Sleep call is commented out in the go server code, the duplication does
not happen.
The python version is inherently slow to process the requests without sleeping (hence
no call to sleep to represent processing data), so there is no way to test on the
Python server if receiving the websocket messages faster stops the duplication.
This leads me to believe that the issue may be timing related?
The duplication also does not happen if, in the Javascript, the 2 1024 * 1024
statements are replaced with a variable that is pre-calculated, for example
const ws = // websocket
const dataSize = 1024 * 1024;
// rest of code up to:
const data = new Uint8Array(dataSize);
for (let j = 0; j < dataSize; j++) {
I have no idea why any of this is happening, i'm starting to suspect that this is a bug
within Firefox.
Any help would be appreciated.
Thanks.
Edit:
It looks like this is definitely a Firefox bug and there's already a bugzilla report on it here
I work in an event organizing company, our main business is organizing “speed dating” meetings between buyers (retailers and distributors) and manufacturers of food and beverages. We have people that create a schedule for events and I would like to somehow automate this process.
I would ask for help with logic for a web app that would schedule the meetings.
There are 10 different companies on each side.
The meetings should be only if one side chose a company from the other side.
Each meeting is 15 minutes.
The whole event should we 2-2,5 hours long.
Any suggestions on how to create a great schedule automatically?
P.S. I am sorry if my question is not clear, this is my first Stack Overflow question.
I don't have a JavaScript solution, but this can be solved with mathematical optimization tools.
Let's start with some data:
---- 11 SET b buyers
buyer1 , buyer2 , buyer3 , buyer4 , buyer5 , buyer6 , buyer7 , buyer8 , buyer9 , buyer10
---- 11 SET s sellers
seller1 , seller2 , seller3 , seller4 , seller5 , seller6 , seller7 , seller8 , seller9
seller10
---- 11 SET r rounds
round1, round2, round3, round4, round5, round6, round7, round8
---- 11 PARAMETER wantMeeting a meeting has been requested
seller1 seller2 seller3 seller4 seller5 seller6 seller7 seller8 seller9
buyer1 1 1
buyer2 1 1
buyer3 1 1
buyer4 1 1
buyer5 1 1 1
buyer6 1 1 1
buyer7 1 1 1 1
buyer8 1 1
buyer9 1 1 1
buyer10 1 1
+ seller10
buyer8 1
Introduce binary variables:
x(b,s,r) = 1 if buyer b meets seller s in round r
0 otherwise
We only consider the cases where wantMeeting=1. Implicitly, when wantMeeting=0, we assume x(b,s,r)=0.
Constraints:
Meeting is requested:
sum(r, x(b,s,r)) = 1 for all b,s with wantMeeting(b,s)=1
Buyer can only have one meeting per round
sum(s|wantMeeting(b,s)=1, x(b,s,r)) <= 1 for all b,r
Seller can only have one meeting per round
sum(b|wantMeeting(b,s)=1, x(b,s,r)) <= 1 for all s,r
Here | is the mathematic notation for "such that".
I have also added some constraints, and an objective to minimize the number of rounds needed. The resulting Mixed Integer Programming model gives as result:
---- 42 VARIABLE x.L meetings
round1 round2 round3 round4
buyer1 .seller1 1
buyer1 .seller9 1
buyer2 .seller5 1
buyer2 .seller7 1
buyer3 .seller3 1
buyer3 .seller4 1
buyer4 .seller1 1
buyer4 .seller3 1
buyer5 .seller2 1
buyer5 .seller4 1
buyer5 .seller6 1
buyer6 .seller5 1
buyer6 .seller6 1
buyer6 .seller9 1
buyer7 .seller1 1
buyer7 .seller2 1
buyer7 .seller5 1
buyer7 .seller6 1
buyer8 .seller3 1
buyer8 .seller8 1
buyer8 .seller10 1
buyer9 .seller2 1
buyer9 .seller5 1
buyer9 .seller6 1
buyer10.seller7 1
buyer10.seller9 1
---- 42 VARIABLE round.L round is used
round1 1, round2 1, round3 1, round4 1
---- 42 VARIABLE numRounds.L = 4 number of rounds needed
I did not have a capacity per round (say n tables are available). This is not very difficult to add. More details are here.
For some more examples of these type of models see:
Scheduling Business Dinners
Speed Dating Scheduling
I probably would solve this on a server, but if you insist on a JavaScript solution, there is a JavaScript port of the GLPK Mixed Integer Programming Solver (link). You may also look into Constraint Programming Solvers (there are a few available in JavaScript).
So I am writing this loop, and I have a random number generator in it. How come it only picks one random number for all the iterations of the loop instead of a new random number? here's my code:
for ( i = 0; i < 25; i++ ) {
var randNum = 0;
var randNum = Math.floor(Math.random() * 5);
}
this is in the chrome browser. Is there something wrong with this code?
Is there something wrong with this code?
"It does absolutely nothing" would be the first thing wrong with it.
If this is your "simplified" version of the code, you've "simplified" away important context (such as how you try and use randNum).
If I run that code, I get random numbers:
1 2 2 0 2 1 2 2 1 0 1 0 3 4 2 1 0 3 3 1 1 4 1 2 4
How are you reading the variable? I used this code:
<script type="text/javascript">
for ( i = 0; i < 25; i++ ) {
var randNum = 0;
var randNum = Math.floor(Math.random() * 5);
document.write(randNum + "\n");
}
</script>
We need a bit more context.
You're probably using the randNum variable inside an anonymous function within the loop.
Since the loop always assigns to the same variable, any calls to any of the anonymous functions after the loop finishes will see only the last number.
To fix this, move the body of the loop into a separate function (either named or anonymous).
Tried it with jQuery output and it works:
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<p></p>
<script>
for ( i = 0; i < 25; i++ ) {
var randNum = 0;
var randNum = Math.floor(Math.random() * 5);
$("p").append(" "+randNum);
}
</script>
</body>
</html>
1st time i get output like
4 2 4 1 1 3 3 2 0 1 4 0 3 4 3 1 4 1 1 2 1 1 2 4 2
second:
1 4 1 3 1 3 0 0 2 1 2 1 4 3 2 0 3 3 0 4 4 3 3 4 4
looks fine for me, maybe problem is in diffrent place.
remove the duplicate var in randNum, but on my chrome it works on both cases. Using the mac version. This is probably related to how you are using the randNum, are you using it outside the for somehow? Your code does nothing (like writing the number) atm.