Get values according function parameters in Javascript - javascript

I have the next function:
const fun = (nr, car, color, obj) => {
const cars = nr - 2;
const colors = nr - 1;
const objects = nr -3;
const main = {
c: `${cars}cars.`,
co: `${colors}colors.`,
obj: `${objects}objects.`
};
console.log(car && main.c, color && main.co, obj && main.obj)
return `${car && main.c || color && main.co|| obj && main.obj}`;
};
console.log(fun(65, false, true, true))
How you can notice, depending by the parameters fun(65, false, true, true) i should get the correct data. In this example i should get this string 64colors. 62obj., but i don't get this. So depending by the parameters i want to show the corresponding values. How to do this?

Since you are using the or operator within the string, it just returns the result till a first non falsy value and not any further. You need to split your logic into three parts to be able to create a proper string
const fun = (nr, car, color, obj) => {
const cars = nr - 2;
const colors = nr - 1;
const objects = nr -3;
const main = {
c: `${cars}cars.`,
co: `${colors}colors.`,
obj: `${objects}objects.`
};
console.log(car && main.c, color && main.co, obj && main.obj)
return `${car && main.c || ''}${color && main.co || ''}${obj && main.obj || ''}`;
};
console.log(fun(65, false, true, true))

main.colors would return undefined which is falsy because it doesn't exist in the main object.
And the OR || operator doesn't return more than one value.
I changed the return statement so you can get 64colors62objects
const fun = (nr, car, color, obj) => {
const cars = nr - 2;
const colors = nr - 1;
const objects = nr -3;
const main = {
c: `${cars}cars.`,
co: `${colors}colors.`,
obj: `${objects}objects.`
};
return `${car && main.c ? main.c : ''}${color && main.co ? main.co : ''}${obj && main.obj ? main.obj : ''}`;
};
console.log(fun(65, false, true, true))

Related

Cannot get minimax function to work for tic tac toe game

const grabEmptySquares = (array) => {
var emptyGameSquares = [];
for (i = 0; i < 9; i++) {
if (!array[i]) emptyGameSquares.push(i);
}
return emptyGameSquares;
};
function findBestMove(board) {
var bestMove = {
index: null,
evaluation: null,
};
var availableMoves = grabEmptySquares(board);
availableMoves.forEach((move) => {
const simulGameboard = JSON.parse(JSON.stringify(board));
simulGameboard[move] = "o";
const evaluation = minimax(simulGameboard, 1, false);
const moveDetails = {
index: move,
evaluation: evaluation,
};
console.log(moveDetails)
if (evaluation > bestMove.evaluation || bestMove.evaluation === null) {
bestMove.index = move;
bestMove.evaluation = evaluation;
}
});
return bestMove.index;
}
function evaluate(board, isMaximizingPlayer, depth) {
var gameStatus = isGameOver(board);
if (gameStatus[0] != true) return;
if (gameStatus[1] === "win")
return isMaximizingPlayer ? +10 - depth : -10 + depth;
if (gameStatus[1] === "tie") return 0;
}
function minimax(board, depth, isMaximizingPlayer) {
var gameStatus = isGameOver(board);
if (gameStatus[0] == true) {
const evaluation = evaluate(board, !isMaximizingPlayer, depth);
return evaluation;
}
var simulGameboard = JSON.parse(JSON.stringify(board));
var availableMoves = grabEmptySquares(simulGameboard);
if (isMaximizingPlayer) {
bestVal = -Infinity;
availableMoves.forEach((move) => {
depth % 2 === 0
? (simulGameboard[move] = "o")
: (simulGameboard[move] = "x");
value = minimax(simulGameboard, depth + 1, false);
bestVal = Math.max(bestVal, value);
const moveDetails = {
index: move,
evaluation: bestVal,
depth: depth,
};
console.log(moveDetails);
});
return bestVal;
} else {
bestVal = Infinity;
availableMoves.forEach((move) => {
depth % 2 === 0
? (simulGameboard[move] = "o")
: (simulGameboard[move] = "x");
value = minimax(simulGameboard, depth + 1, true);
bestVal = Math.min(bestVal, value);
const moveDetails = {
index: move,
evaluation: bestVal,
depth: depth,
};
console.log(moveDetails);
});
return bestVal;
}
}
function isGameOver(array) {
var gameOver = false;
if (
(array[0] && array[0] === array[1] && array[0] === array[2]) ||
(array[3] && array[3] === array[4] && array[3] === array[5]) ||
(array[6] && array[6] === array[7] && array[6] === array[8])
) {
return (gameOver = [true, "win"]);
}
if (
(array[0] && array[0] === array[4] && array[0] === array[8]) ||
(array[2] && array[2] === array[4] && array[2] === array[6])
) {
return (gameOver = [true, "win"]);
}
if (
(array[1] && array[1] === array[4] && array[4] === array[7]) ||
(array[0] && array[0] === array[3] && array[3] === array[6]) ||
(array[2] && array[2] === array[5] && array[5] === array[8])
) {
return (gameOver = [true, "win"]);
}
if ([...array].every((index) => index)) {
return (gameOver = [true, "tie"]);
}
return (gameOver = [false, null]);
}
I followed https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-3-tic-tac-toe-ai-finding-optimal-move/ for direction, and as far as I can see, the logic is the same.
Still, my code doesn't come up with the correct moves. The evaluation my minimiax function gives to each move is wrong. And it is sooo wrong I cannot even begin to figure out where the code is off. Please help. I've spent the last two weeks working on this.
Ex:
var gameboard = [ null, "o", null, "x", "x", null, null, null, null ]
If I run findBestMove(gameboard), the expected output should be
bestMove = {index: 5,
evaluation: 0}
What I get instead is
bestMove = {index: 1,
evaluation: -8}.
In fact, every single move has the same evaluation.
This isn't the easist code to read, but AFAICT the minimax function copies the game board state once and then loops through possible moves with availableMoves.forEach. This means that when evaluating each possible move, it acts as if each previously considered move had been made. Move the copy inside the forEach and things should make somewhat more sense.
You already have this in the findBestMove function. I'd strongly suggest unifying findBestMove and minimax (and the sides of the isMaximizingPlayer branch inside minimax). Having very similar code in multiple places makes it hard to remember where you have and haven't fixed things.
I'd also suggest replacing the isMaximizingPlayer and depth%2 logic with a player variable that can be either "x" or "o", and multiplying goodness scores by -1 as needed. It'll be easier to keep track of.

Filter array based on filter combinations

I have an array as following
array = [
{
name: 'A'
instructors: [
{
name:'InsA'
}
]
businessUnit: {name:'myBusiness'}
},
{
name: 'B'
instructors: [
{
name:'InsB'
}
]
businessUnit: {name:'myBusinessB'}
}
]
I want to filter this array with the values i have which are also in an array as following
classArr = [A,C,D]
instructorArr = [InsA,InsC,InsZ]
businessName = [myBusinessB,myBusinessX,myBusinessD]
NOTE: These filters can have empty arrays as well. For Ex: businessName = [] .
My current approach is to filter as follows
const filtered = array?.filter(
(activity) =>
classArr?.includes(activity.name) &&
activity.instructors?.some((instructor) =>
instructorArr?.some((instructorFilter) => instructor?.name === instructorFilter),
) &&
businessName?.includes(activity.businessUnit?.name),
);
But the issue is this returns if all conditions are met. Not when just 2 or 1 condition is met. How can i return the filtered array when several or all conditions are met?
const filtered = array?.filter(
(activity) =>
classArr?.includes(activity.name) ||
activity.instructors?.some((instructor) =>
instructorArr?.some((instructorFilter) => instructor?.name === instructorFilter),
) ||
businessName?.includes(activity.businessUnit?.name),
);
In your filtered function
const filtered = array?.filter(
(activity) =>
classArr?.includes(activity.name) &&
activity.instructors?.some((instructor) =>
instructorArr?.some((instructorFilter) => instructor?.name === instructorFilter),
) &&
businessName?.includes(activity.businessUnit?.name),
);
You basically say you want to keep all activities where:
The array includes the class name
AND the array includes the instructor
AND the array includes the business unit
(the && operator is the AND operator).
However you state that you want to 'return the filtered array when several [OR] all conditions are met?'
So to fix this, you should switch out the && (AND) operator with the || (OR) operator like this:
const filtered = array?.filter(
(activity) =>
classArr?.includes(activity.name) ||
activity.instructors?.some((instructor) =>
instructorArr?.some((instructorFilter) => instructor?.name === instructorFilter),
) ||
businessName?.includes(activity.businessUnit?.name),
);
You need to check for the length in your array. Only doing array? with check if its nullish, not the size.
const filtered = array?.filter(
(activity) =>
(
classArr.length ? classArr.includes(activity.name) : false
)
&& (
activity.instructors.length // check for length
? activity.instructors.some((instructor) =>
instructorArr.length // check for length
? instructorArr.some((instructorFilter) => instructor.name === instructorFilter)
: false)
: false
)
&& (
businessName.length ? businessName.includes(activity.businessUnit?.name) : false
)
);
That line is very hard to read and will be hard for anyone else to understand in the future, so lets open it up
let filtered = [];
if (array.length){
for (const index in myArray){
const has_classArr = !!classArr.length;
const has_instructorArr = !!instructorArr.length;
const has_businessName = !!businessName.length; // check for length, if you do "businessName?" it only cheks for its existence
if (has_classArr && has_instructorArr && has_businessName){
const activity = myArray[index];
const has_activityInstructors = activity.instructors && activity.instructors.length
const has_activityBusinessUnit = !!activity.businessUnit
if (has_activityInstructors && has_activityBusinessUnit){
for (const instructor of activity.instructors){
if (instructorArr.includes(instructor.name) && businessName.includes(activity.buninessUnit.name) && classArr.includes(activity.name))
filtered.push(activity)
}
}
}
}
}

Sort function breaks when value is null/undefined (React, javascript)

I prepared a sort functions for my set of data:
const sortHelper = (key, order) => {
return (compare1, compare2) => {
if (!compare1.hasOwnProperty(key) || !compare2.hasOwnProperty(key)) {
return 0;
}
const varA = compare1[key].toString().toUpperCase();
const varB = compare2[key].toString().toUpperCase();
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
return order === 'DESC' ? comparison * -1 : comparison;
};
};
export const sortData = (data, sortOrder) => {
if (!sortOrder) {
return data;
}
const { sortingCriteria, direction } = sortOrder;
return data.sort(sortHelper(sortingCriteria[0], direction));
};
Unfortunately it breaks, when some value is null or undefined. I tried to this like this:
const varA = compare1[key].toString().toUpperCase();
But still it breaks.
You could take a default empty string for falsy values.
const varA = (compare1[key] === 0 ? 0 : compare1[key] || '').toString().toUpperCase();
If you ndo not need only strings, you could take the type directly an use the empty string as default value for falsy value who are not zero.
const varA = compare1[key] === 0 ? 0 : compare1[key] || '';

How to use two integers as a key

I have a memoization problem. There is a value calculated between two objects and I want to store it, such that I can pass in the two number id's from the objects to get the value regardless of the order that I pass in the id's.
I could just store it under both of the id's, or check for both id's, but I was wondering out of curiosity if there was a more efficient solution.
Examples:
Check for both id's:
var storedValues = {};
function generateValue(a, b)
{
if (storedValues[a.id][b.id])
return storedValues[a.id][b.id];
else if (storedValues[b.id][a.id])
return storedValues[b.id][a.id];
else
// Generate value, store it under a.id and return it
}
Store it under both id's:
var storedValues = {};
function generateValue(a, b)
{
if (storedValues[a.id][b.id])
return storedValues[a.id][b.id];
else
// Generate value, store it under a.id and b.id and return it
}
How can I generate a unique map key from two numbers, so I can retrieve the value with those two numbers regardless of the order that I pass them in?
I'm using JavaScript btw.
There are many ways to that, first of all, you can just create a single key for the objectkey e.g.: '11_12'
var storedValues= {};
var id1 = 11;
var id2 = 12;
var calculatedvalue = id1 * id2;
var objectkey = id1+'_'+id2;
storedValues[objectkey] = calculatedvalue;
it is also possible to use nested object:
var storedValues= {};
var id1 = 11;
var id2 = 12;
var calculatedvalue = id1 * id2;
if (storedValues[id1] == null ){
storedValues[id1] = {};
}
storedValues[id1][id2] = calculatedvalue;
More over your ids are always integer you can use Arrays
var storedValuesArr = [];
var id1 = 11;
var id2 = 12;
var calculatedvalue = id1 * id2;
if (!Array.isArray(storedValuesArr[id1])){
storedValuesArr[id1] = [];
}
storedValuesArr[id1][id2] = calculatedvalue;
and in order to return the values regardless of the order:
function generateValue(a, b)
{
if (storedValues[a.id] != null && storedValues[a.id][b.id] !== null)
return storedValues[a.id][b.id]
else if (storedValues[b.id] != null && storedValues[b.id][a.id] !== null)
return storedValues[b.id][a.id];
else
// Generate value, store it under a.id and return it
}
You could sort the keys for setting the value and for getting the value.
function getValue(object, key0, key1) {
return [key0, key1]
.sort((a, b) => a - b)
.reduce((o, k) => (o || {})[k], object);
}
function setValue(object, key0, key1, value) {
var keys = [key0, key1].sort((a, b) => a - b),
last = keys.pop();
object[keys[0]] = object[keys[0]] || {};
object[keys[0]][last] = value;
}
var object = {};
setValue(object, 3, 7, 42);
console.log(getValue(object, 3, 7));
console.log(getValue(object, 7, 3));
console.log(object);
Different approach by using the keys a given.
function getValue(object, key0, key1) {
return key0 in object ? object[key0][key1] : object[key1][key0];
}
function setValue(object, key0, key1, value) {
object[key0] = object[key0] || {};
object[key0][key1] = value;
}
var object = {};
setValue(object, 3, 7, 42);
console.log(getValue(object, 3, 7));
console.log(getValue(object, 7, 3));
console.log(object);

How can I sort an ImmutableJS List object on multiple keys?

I have a list of work history objects that have start and end date fields. I need to sort by start and then by end. If the dates overlap between work history objects when I use back-to-back sort() functions, the order can potentially be off. I need a way to sort an ImmutableJS List by multiple keys where the second sort key is only processed when first key values are equal. I would have assumed that ImmutableJS would have a simpler way to handle this type of sorting. Here is what I came up with, but this feels horrible to me (using momentJS for date comparison):
const sortedWorkHistory = workHistory.sort((b, a) => {
const aTo = moment(a.get('to') === null ? undefined : a.get('to'));
const bTo = moment(b.get('to') === null ? undefined : b.get('to'));
if (aTo.isBefore(bTo)) {
return -1;
}
if (bTo.isBefore(aTo)) {
return 1;
}
return 0;
})
.sort((b, a) => {
const aTo = moment(a.get('to') === null ? undefined : a.get('to'));
const bTo = moment(b.get('to') === null ? undefined : b.get('to'));
if (aTo === bTo) {
const aFrom = moment(a.get('from'));
const bFrom = moment(b.get('from'));
if (aFrom.isBefore(bFrom)) {
return -1;
}
if (bFrom.isBefore(aFrom)) {
return 1;
}
return 0;
}
});
You could put that into a single compare function like this:
const sortedWorkHistory = workHistory.sort((b, a) => {
const aTo = moment(a.get('to'));
const bTo = moment(b.get('to'));
const aFrom = moment(a.get('from'));
const bFrom = moment(b.get('from'));
if (aTo.isBefore(bTo)) {
return -1;
} else if (bTo.isBefore(aTo)) {
return 1;
} else if (aFrom.isBefore(bFrom)) {
return -1;
} else if (bFrom.isBefore(aFrom)) {
return 1;
}
return 0;
});
Or maybe even
const sortedWorkHistory = workHistory.sort((b, a) => {
// aTo = ...
if (aTo.isSame(bTo)) {
return aFrom.diff(bFrom);
}
return aTo.diff(bTo);
});
aTo === bTo is never going to be equal the way you are defining it. You're comparing references to objects which are always different. You'll need to use moments .isSame method. You can verify that by trying this:
const a = moment().startOf('day');
const b = moment().startOf('day');
console.log(a === b); // false
console.log(a.isSame(b)); // true
Here is what I'd do:
// Helper function to create moments
function parseMoment(date) {
return moment(date === null ? undefined : date);
}
const sortedWorkHistory = workHistory
.sort((a, b) => parseMoment(a.get('to')).diff(parseMoment(b.get('to'))))
.sort((a, b) => {
if(parseMoment(a.get('to')).isSame(parseMoment(b.get('to')))) {
return parseMoment(a.get('from')).diff(parseMoment(b.get('from')))
}
return 0;
});

Categories

Resources