Creating a constraint check in Angular - javascript

I have a list of questions that can be put into a random order by the system but at the same time there are checks for certain questions that need to come before a different question ex.
q1 before q4, q3 before q2
I have to also make sure that there isn't something like this in there though:
q1 before q4, q3 before q2, q2 before q1, q4 before q2
I'm not entirely sure how this is going to be done.
A way of storing this I was given as an example would be a number array of arrays let n: number[number, number][]

If you're lazy, which I am, since there are only a few questions, I would just randomly order the questions and check against the rules, if it violates them just run it again.
If this is a homework assignment and you'll be marked down for being lazy, you can create a function which randomly assigns questions to slots in an array, starting with questions which must come after the others, then reduce the range of the random number generator for the ones which must be less to have a maximum of the randomly generated position the one which must be greater than appears in.
For example:
lets say you have an array of questions of length 5. and q1 has to appear before q3 and q4 has to appear before q2. First place q3 in a random position in an array of length 5, lets say that poition is 2. Then randomly assign q1 to a position less than 2. Do that for each set of rules, each time a number is assigned remove it from the RNG, once all the rules are completed run the other questions.
You can represent a constraint as a tuple with the first string representing the question which must come first and the second as the question which must come second. rules: number[][] = [['q1', 'q3], ['q4', 'q2']] would be your rules and you would have all questions in a question array questions: string[] = ['q1', 'q2', ...] each time a question is assigned from the tuples remove it from the questions array using splice getting the index of the string. Heres some code which should work with those two data types, notice I use an array of available indices to exclude previously chosen indices:
questions: string[]; // put all questions here.
rules: string[][]; // put all less than rules as tuples here,
// i.e. q1 must come before q2 written as [['q1', 'q2'],...]
randomizer(): string[] {
const arr = new Array<string>(this.questions.length);
const availableIndices = [];
let rand: number;
for (let i = 0; i < arr.length; i++) {
availableIndices.push(i)
}
for (const tuple of rules) {
rand = Math.floor(Math.random() * availableIndices.length)
arr[availableIndices[rand]] = tuple[1];
questions.splice(indexOf(tuple[1]), 1);
availableIndices.splice(indexOf(rand), 1);
rand = Math.floor(Math.random() * availableIndices.indexOf(tuple[1]))
arr[availableIndices[rand]] = tuple[0];
questions.splice(indexOf(tuple[2]), 1);
availableIndices.splice(indexOf(rand), 1);
}
for (const q of questions) {
rand = Math.floor(Math.random() * availableIndices.length);
arr[availableIndices[rand]] = q;
availableIndices.splice(indexOf(rand), 1);
}
return arr;
}
This algorithm is O(n) so your teacher can't complain.

Related

How to find numbers and remove them from a multidimensional array?

I'm trying to create a Bingo game to play with my family and I would like to know what is the best way to remove numbers that have been called and then create another array with the tickets that is empty - so I know they have already win.
So far I made this array (I reduced it to make easier to understand):
var tickets = [
['T1',1,2,8,10,23,18,44],
['T2',2,3,3,6,45,12,55],
['T3',4,7,10,42,55,45,60],
['T4',3,5,3,6,55,68,85],
['T5',1,4,3,6,66,74,80],
];
All the other part of bingo is done, I just need to realize how can I know the tickets that have finished already to make easier to validate the winners.
Do you have an array of numbers that have been already called?
If so:
var called = [1,2,8,10,23,18,44];
var tickets = [
['T1',1,2,8,10,23,18,44],
['T2',2,3,3,6,45,12,55],
['T3',4,7,10,42,55,45,60],
['T4',3,5,3,6,55,68,85],
['T5',1,4,3,6,66,74,80]
];
// Gets tickets with called numbers removed
tickets = tickets.map(x => x.filter(x => !called.includes(x)));
console.log(tickets);
// Gets winners
var winners = tickets.map(x => x.length === 1 ? x : []).flatMap(x=>x)
console.log(winners);
// You should really use objects here but you decided multidimentional arrays, Example of an object
/*
{
'T1': [1,2,8,10,23,18,44],
'T2': [2,3,3,6,45,12,55],
'T3': [4,7,10,42,55,45,60],
'T4': [3,5,3,6,55,68,85],
'T5': [1,4,3,6,66,74,80]
}
*/
/* this is to make the SO console bigger */
.as-console-wrapper{max-height: none !important;height:100% !important}

How do I find the Max and Min values of a row that my if statement has been applied to?

So I was given this yesterday and this works brilliantly but I have been asked to bold the minimum and maximum value on the row that my function below has been applied. I have 3 different datasets with different values on different rows, so I can't just ask it to bold row 7. I need to demonstrate that this can be applied to any given data. Does anyone know how to achieve this, my data is between C3:W16 for all 3 datasets to save confusion.
function searchOutput() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getRange("C3:W16").getValues();
data.forEach(function (row, i)
{row.forEach(function (cell, j)
{if(cell >= 5 && row[j + 1] < 5){sheet.getRange(i + 3, j + 3, 1, 2).setFontWeight("bold")
{if(cell Math.max(...row)).getRange(i + 3, j + 3, 1, 1).setFontWeight("bold")
{if(cell Math.min(...row)).getRange(i + 3, j + 3, 1, 1).setFontWeight("bold")}}}})})
}
There's a 3-step approach to optimally processing multiple rows:
Fetch arrays of values, formatting or other state from the sheet using a bulk getter method.
Process and update the fetched arrays.
Write the updated values, formatting or other state back to the sheet using a bulk setter method.
This is necessary b/c invoking sheet methods like getRange() and setFontWeight() multiple times within a loop is computationally expensive.
So the first thing you need to figure out is which bulk getter and setter methods are applicable to your use case.
In your scenario you need to find the min and max of values in a row so getValues() is a must have. And you want to update the font weight of those cells so getFontWeights() and setFontWeights() are also necessary.
TIP If you need to find a getter or setter method for other resources check out the documentation for the Range object. The general rule is that any method with get or set as its prefix and ends in s is a bulk getter or setter method.
Now that you know what you need, its fairly simple to write the code:
function boldMinMax() {
var range = SpreadsheetApp.getActiveSheet().getRange("C3:W16");
// fetch array of arrays of row values and array of arrays of font weights in bulk using getter methods
var valueRows = range.getValues();
var fontWeightRows = range.getFontWeights();
// process fetched arrays updating font weights for min and max cells
valuesRows.forEach(function(valueRow, rowIndex) {
// Only need to calculate min and max once per row
var min = Math.min(...valueRow);
var max = Math.max(...valueRow);
// find and update fontWeightRow
var fontWeightRow = fontWeightRows[rowIndex];
valueRow.forEach(function(value, cellIndex) {
if (value === min || value === max) {
fontWeightRow[cellIndex] = 'bold';
}
});
});
// Write updated font weights back to sheet
range.setFontWeights(fontWeightRows);
}
That should be enough to get you sorted.

Checking an input against a specific array string

I am trying to create a quiz that randomly selects a question from pool of questions in an array, which is answered in an input box that is to be checked against the corresponding answer string in the array. I used math.floor(math.random() *); to get my random number. The random number is intended to be used to find both the question and answer, which are arranged in order to correspond to one another, e.g. ['question1','question2'] and ['answer1','answer2'].
I am having difficulty with trying to get my input to properly be checked against the corresponding answer value from the array. I am fairly novice at Javascript, so I am not sure as to how to do this. I tried using the document.getElementById command to compare the two. I suspect it has something to do with the fact that ansNum doesn't get the value of questNum because of the fact that questNum is only given its value inside the generateQuiz function. (I realize ansNum is likely redundant, but I was just playing around to see if anything would happen)
Javascript:
const questions = ['What do young Roman males wear?','Who is the Roman god of the smith?','Who is the 6th king of Rome?'];
const answers = ['toga praetexta','vulcan','servius tullius'];
function getQuestNum() {
questNum = Math.floor(Math.random() * 3);
};
function getAnsNum() {
ansNum = questNum();
}
function generateQuiz() {
getQuestNum();
document.getElementById("question").innerHTML = questions[questNum];
};
function checkCorrect() {
getAnsNum();
if (answer[ansNum] = document.getElementById("input").innerHTML) {
document.getElementById("verification").innerHTML = "Correct!";
}
};
Codepen Link
An image of the code
Based on your code, I fixed it with some changes. It is not the best way to do this i think. I posted the js part here.
const questions = ['What do young Roman males wear?','Who is the Roman god of the smith?','Who is the 6th king of Rome?'];
const answers = ['toga praetexta','vulcan','servius tullius'];
var questNum;
function getQuestNum() {
questNum = Math.floor(Math.random() * 3);
};
function getAnsNum() {
ansNum = questNum;
}
function generateQuiz() {
getQuestNum();
document.getElementById("question").innerHTML = questions[questNum];
};
function checkCorrect() {
getAnsNum();
if (answers[ansNum] = document.getElementById("input").value) {
document.getElementById("verification").innerHTML = "Correct!";
}
};
First you need a global variable questNum then you can use it in all of your functions.
The function getAnsNum() is redundant, at least i think so, just use questNum in your checkCorrect() function.
For getElementByID function, insert an ID attribute to your input
<input id="input" type="text" name="input">
For input, if you want to take the value of the input field, use document.getElementById("input").value instead of innerHTML.
If you not sure about any result, console.log it or use Chrome dev debug tool to check the result. In the checkCorrect function, your array name should be answers instead of answer.
Shorter ver:
const questions = ['What do young Roman males wear?','Who is the Roman god of the smith?','Who is the 6th king of Rome?'];
const answers = ['toga praetexta','vulcan','servius tullius'];
var questNum;
function getQuestNum() {
questNum = Math.floor(Math.random() * 3);
};
function generateQuiz() {
getQuestNum();
document.getElementById("question").innerHTML = questions[questNum];
};
function checkCorrect() {
if (answers[questNum] = document.getElementById("input").value) {
document.getElementById("verification").innerHTML = "Correct!";
}
};
It would be simpler to create an array of objects that each contain both a question and an answer - and create a function that generates your random number and returns the object at the corresponding index.
Then you'll have access to everything you need without worrying about whether or not you can maintain access to the original randomly selected number, or matching up indices between two different arrays.

sorting two associative arrays/stacks

I am implementing an algorithm I designed and am exploring different approaches
This isn't a homework problem but I am going to explain it like one: lets say a merchant has bought inventory of apples on different days, and also sold some on different days. I want the weighted average timestamp of their current purchases.
I am storing this data object as timestamp string in epoch time, and quantity of apples. My dataset actually has the purchases and the sells in separate data sets, like so:
//buys
var incomingArray = {
"1518744389": 10,
"1318744389": 30
};
//sells
var outgoingArray = {
"1518744480": 3,
"1418744389": 5,
"1408744389": 8
};
and I would like the outcome to show only the remainding incomingArray timestamp purchase pairs.
var incomingArrayRemaining = {
"1518744389": 7,
"1318744389": 17
};
Where you see there was one outgoing transaction for 3 apples at a later timestamp, therefore subtracting from 10. And there were 13 outgoing transactions before the buy of 10, but after the purchase of 30, so they only subtract from the 30.
Note, if more than 10 were transferred after 10, it would subtract from both 10 and 30. The number of apples can never be less than 0.
First, to accomplish my goals it seems that I need to know how many are actually still owned from the lot they were purchased in.
Instead of doing stack subtracting in the LIFO method, it seems like this has to be more like Tax Lot Accounting. Where the lots themselves have to be treated independently.
Therefore I would have to take the timestamp of the first index of the sell in the outgoing array and find the nearest older timestamp of the buy in the incoming array
Here is what I tried:
for (var ink in incomingArray) {
var inInt = parseInt(ink);
for (var outk in outgoingArray) {
if (inInt >= 0) {
var outInt = parseInt(outk);
if (outInt >= inInt) {
inInt = inInt - outInt;
if (intInt < 0) {
outInt = inInt * -1; //remainder
inInt = 0;
} //end if
} //end if
} //end if
} //end innter for
} //end outer for
It is incomplete and the nested for loop solution will already have poor computational time.
That function merely tries to sort the transactions so that only the remaining balance remains, by subtracting an outgoing from the nearest incoming balance, and carrying that remainder to the next incoming balance
I feel like a recursive solution would be better, or maybe something more elegant that I hadn't thought of (nested Object forEach accessor in javascript)
After I get them sorted then I need to actually do the weighted average method, which I have some ideas for already.
First sorting, then weighted average of the remaining quantities.
Anyway, I know the javascript community on StackOverflow is particularly harsh about asking for help but I'm at an impasse because not only do I want a solution, but a computationally efficient solution, so I will probably throw a bounty on it.
You could convert the objects into an array of timestamp-value pairs. Outgoing ones could be negative. Then you can easily sort them after the timestamp and accumulate how you like it:
const purchases = Object.entries(incomingArray).concat(Object.entries(outgoingArray).map(([ts, val]) => ([ts, -val])));
purchases.sort(([ts1, ts2]) => ts1 - ts2);
Now you could iterate over the timespan and store the delta in a new array when the value increases (a new ingoing):
const result = [];
let delta = 0, lastIngoing = purchases[0][0];
for(const [time, value] of purchases){
if(value > 0){
// Store the old
result.push([lastIngoing, delta]);
// Set up new
delta = 0;
lastIngoing = time;
} else {
delta += value;
}
}

Create Groups Without Repeating Previous Groupings

I have created a random group creator, but random doesn't really guarantee that you work with people you haven't worked with before. If someone was able to generate a "Random Group Generator With History" that tracked previous groups and avoided putting people in groups with the same people over and over, I would definitely use it! Does anyone know how to do this?
For clarity: Given an array of strings
["Jason", "Kim", "Callie", "Luke"]
and an array of previous pairings (also arrays)
[[["Jason", "Kim"], ["Callie", "Luke"]], [["Jason", "Luke"], ["Callie", "Kim"]]]
return groupings with the fewest number of repeat group members
[["Jason", "Callie"], ["Luke", "Kim"]]
I'm imagining that the number I am trying to minimize is the number of repeat partners. So for each pair of two people, for every time they have already been on a team, if the result puts them on the same team, the result would have a score of that. For the example, the "scoring" to arrive at the return value could look like this:
["Jason", "Kim"] have a score of 1, they have been paired together before
["Callie", "Luke"] have a score of 1, they have been paired together before
["Jason", "Luke"] have a score of 1, they have been paired together before
["Callie", "Kim"] have a score of 1, they have been paired together before
["Jason", "Callie"] have a score of 0, they have not been paired together before
["Luke", "Kim"] have a score of 0, they have not been paired together before
Choose the sets that cover the entire list while generating the smallest score. In this case, the pairings ["Jason", "Callie"] and ["Luke", "Kim"] cover the entire set, and have a score of 0 (no repeated groupings) and therefore it is an optimal solution (0 being the best possible outcome).
This is probably the wrong way to do this (since I'm imagining it would take n squared time), but hopefully it gives a sense of what I'm trying to optimize for. This would not need to be a perfect optimization, just a "decent answer" that doesn't put the same groups together every single time.
Ideally, it would be able to handle any size group, and also be able to handle the fact that someone might be out that day (not all people will be in all of the arrays). I would love a javascript answer, but I should be able to translate if someone can come up with the logic.
You could collect all pairings in an object and count. Then take only the ones with a smaller count.
function getKey(array) {
return array.slice().sort().join('|');
}
var strings = ["Jason", "Kim", "Callie", "Luke"],
data = [[["Jason", "Kim"], ["Callie", "Luke"]], [["Jason", "Luke"], ["Callie", "Kim"]]],
object = {},
i, j,
keys;
for (i = 0; i < strings.length - 1; i++) {
for (j = i + 1; j < strings.length; j++) {
object[getKey([strings[i], strings[j]])] = 0;
}
}
data.forEach(function (a) {
a.forEach(function (b, i) {
object[getKey(b)]++;
});
});
keys = Object.keys(object).sort(function (a, b) {
return object[b] - object[a];
});
keys.forEach(function (k) {
console.log(k, object[k]);
});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources