How to fix Euclidean Distance function? (Movie Recommender) - javascript

Something similar to this https://blazingedge.io/blog/movie-recommendation-javascript/
My ecDistance function is getting small similarity scores
For example: .01 - 0059 and smaller. The logic is correct. What I think may be wrong is looping through all of the data and using it in user2 to compare each one in a loop.
ecDistance Function takes in user1 which is from the command line or
process.argv[2] and user2 all the of the movies.'
user2:
//object
[{
userId: 1
movieId:1
rating:4.5
}, ...]
function ecDistance(user1, user2) {
let objRating = getComparedRating(user1, user2) // function that handles all the ratings that are the same from both users
let totoalOfSums = 0
let tempSim = []
//user2 is all of the users besides user one [{ userId:1 , movieId:1, rating:4.5}]
for (let i = 0; i < user1.length; i++) { // user1 is what is taken in from process.argv[2] {
let diff = objRating.user1[i] - objRating.user2[i]
let squared = diff * diff
totalOfSums += squared
let squareRootSum = Math.sqrt(totalofSums)
let similarity = 1 / (1 + squareRootSum) // get the similarity score
tempSim.push(similarity)
}
}
function getComparedRating(user1, user2) {
let userRating1 = []
let userRating2 = []
user2.forEach((data) => {
user1.forEach((main) => {
/*
if //they have the same movieId and if they are not the same user,
push them in two different variables to use later on.
*/
if (data.movieId == main.movieId && data.userId !== main.userId) {
userRating1.push(main.rating)
userRating2.push(data.rating)
}
})
})
}
return {
user1: userRating1,
user2: userRating2
}
my expected Result are values, less than or equal to 1. (depending on the rating)

Related

Looking for a Cleaner Way To Make this Pagination Function in JavaScript

I want to write a method that takes in an offset and limit along with a passed in Array of objects. The objects always have an id column. I want to return a new Array with the result based on the offset and limit. Here is an example, followed by my implementation that I'm not happy with (too many fences and fence post like variables which are always error prone). Also, if limit == -1 then take the rest.
Maybe there is a better way with slice? Could reduce somehow help?
const baseArray = [{name: 'peter'},{name: 'terry'},{name: 'tammy'},{name: 'mary'}];
const offset = 1;
const limit = 2;
const speakerArray = getPaginatedData(baseArray,offset,limit);
speakerArray is [{name: 'terry',cursor: 'dGVycnk='},{name: 'tammy', cursor: 'dGFtbXk='];
where, the cursor's are calculated with this line of code:
console.log(Buffer.from('terry').toString("base64"));
Here is my implementation I don't like.
const speakerArray = baseArray
.filter((rec, index) => {
return index > offset - 1 && (offset + limit > index || limit == -1);
})
.map((rec) => {
rec.cursor = Buffer.from(rec.name.toString()).toString("base64");
return rec;
});
Well, the implementation with slice is
function getPaginatedData(array, offset, limit) {
if(limit < 0) return array.slice(offset);
return array.slice(offset, offset + limit);
}
const baseArray = [
{ name: "peter" },
{ name: "terry" },
{ name: "tammy" },
{ name: "mary" },
];
const offset = 1;
const limit = 2;
const speakerArray = getPaginatedData(baseArray, offset, limit);

Get a total value from an array containing both strings ('A','J'...) and numbers (deck of cards in an array)

I have an issue where I have an array containing a deck of cards (['A', 2,3,...'J',...])
I want to be able to pick a number of random cards and then get the total sum of them. for example J,4 should give me the total value of 14.
my current problem is that I can't figure out how to change the strings in the array to a number and
then add those together to get the total sum.
my current code is:
blackjackGame={
'you': 0,
'cards': ['A','2','3','4','5','6','7','8','9','10','J','Q','K'],
'cardsMap' : {'A':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, '10':10, 'J':10, 'Q':10, 'K':10},
}
let playerCards = 2
let card = [];
const YOU = blackjackGame['you']
// gives me a random card
function randomCard (){
let rand = Math.floor(Math.random()* 13)
return blackjackGame['cards'][rand];
}
// gives me the two starting cards for the player in an array so I can later add more
function start(){
for(let i= 0; i < playerCards; i++){
card.push(randomCard())
}
return card
}
function totalValue (player){
// this is where i have no idea what to do
// let player = card.reduce(function (a,b){
// return a +b
// }, 0)
// return player += blackjackGame['cardsMap'][card[0]]
}
console.log(start())
console.log(showScore(YOU)) ```
PS. I'm trying to create a blackjack game.
Your reduce code is fine. Just add the reference to blackjackGame.cardsMap to retrieve the value that corresponds to card b.
let sum = card.reduce(function(a, b) {
return a + blackjackGame.cardsMap[b];
}, 0);
Note that you cannot return that value via the argument of the function. Instead let the function return it with a return statement:
return sum;
const blackjackGame={
'you': 0,
'cards': ['A','2','3','4','5','6','7','8','9','10','J','Q','K']
}
let playerCards = 2
let card = [];
const YOU = blackjackGame['you']
function getCardValue(card) {
const v = blackjackGame['cards']
if(v.indexOf(card) === -1){
throw Error("not found")
}
// for each card above index 9 (10), always return 10
return v.indexOf(card) > 9 ? 10 : v.indexOf(card) + 1
}
function randomCard (){
let rand = Math.floor(Math.random()* 13)
return blackjackGame['cards'][rand];
}
function deal(){
for(let i= 0; i < playerCards; i++){
card.push(randomCard())
}
return card
}
function calculateValue (cards){
return cards.reduce(function (total, num){
return total + getCardValue(num)
}, 0)
}
document.getElementById('deal').addEventListener('click',(e) => {
const cards = deal()
console.log(cards)
const playerValue = calculateValue(cards)
YOU = playerValue
console.log(playerValue)
})
<html>
<head>
</head>
<body>
<button id="deal">Deal</button>
<span id=cards />
<span id=total />
</body>
</html>
You need a way to map the face to the value. This will work:
function getValueOfCard( face ) {
var cardOrder =" A234567891JQK";
var faceStart = (""+face).substring(0,1);
return Math.min(10, cardOrder.indexOf(faceStart))
}
If you want to get the values of all your cards, simply iterate over them (faster than reduce, and more easy to read).
Your card only needs the face and color, the other values follow.
card = { color: "spades", face : "King" };
getValueOfCard( card.face );
function totalValue ( playerHand ){
// assuming an array of cards is the player hand
var total = 0;
for ( var card in playerHand ) {
total += getValueOfCard( card.face );
}
return total;
}
I also recommend, that you create all your cards in one go, and then shuffle them, by picking two random numbers and switching these two cards. Do this in a loop for a couple of times, and you have a randomized stack of cards, which means you can actually treat it as a stack.
cardColors = ["♠","♥","♦","♣"];
cardFaces = ['A','2','3','4','5','6','7','8','9','10','J','Q','K'];
// create deck of cards
var stackOfCards = [];
for ( var a = 0; a < cardColors.length; a++ ) {
var curColor = cardColors[a];
for ( var i = 0; i < cardFaces.length; i++) {
var curFace = cardFaces[i];
card = { color : curColor, face : curFace };
stackOfCards.push(card);
}
}
// shuffle the deck
// then you can pop cards from the stack to deal...
function start () {
for (let i = 0; i < playerCards; i++) {
cards.push(randomCard())
}
totalValue(cards)
}
function totalValue (cards) {
cards.forEach((card) => {
blackjackGame.you += blackjackGame.cardsMap[card]
})
}
start()
console.log(blackjackGame.you)
You were on the right track with having a map. You can access objects with a variable by using someObj[yourVar]

coin-base pro web socket current copy of order book (level 2)

hi is it possible to keep an up to date copy of the order book in an array from the level 2 web socket feed similar to how it is on the website or in the first message returned as snapshot
snapshot returned from level 2 update as type="snapshot"
this is what i am trying to recreate using the type="l2update"
the question is how do i use the l2updade to keep an accurate copy of the order book or at least get the top level ask and bid from the web socket instead of poling the "getProductOrderBook"
const websocket = new CoinbasePro.WebsocketClient(
["BTC-EUR"],
"wss://ws-feed.pro.coinbase.com",
null,
{ channels: [
{
"name": "level2"
}
]
});
websocket.on('message', data => {
var bids = [];
var asks = [];
var x= 0;
var x2 = 0;
if(data.changes[0][0] == "buy"){
if(x <= 100){
bids[x] = parseFloat(data.changes[0][1]);
x++;
}else{
x = 0;
bids[x] = parseFloat(data.changes[0][1]);
x++;
}
}
if(data.changes[0][0] == "sell"){
if(x2 <= 100){
asks[x2] = parseFloat(data.changes[0][1]);
x2++;
}else{
x2 = 0;
asks[x2] = parseFloat(data.changes[0][1]);
x2++;
}
}
console.log(data);
});
To do it directly in the array is useless complicated. I would do it in an object and would convert the object to an array when required. Something like (I assume product_id is always BTC-EUR):
let asks;
let bids;
websocket.on("message", data => {
if(data.type == "snapshot") {
asks = {};
bids = {};
data.asks.forEach(([price, amount]) => (asks[price] = parseFloat(amount)));
data.bids.forEach(([price, amount]) => (bids[price] = parseFloat(amount)));
return;
}
if(data.type == "l2update") data.changes.forEach(change => {
const [action, price, amount] = change;
const obj = action == "buy" ? bids : asks;
const parsed = parseFloat(amount);
if(parsed) obj[price] = parsed;
else delete obj[price];
});
});
Than you can always get the arrays with (or vice versa, didn't tested):
Object.entries(asks).map(([pr, am]) => [parseFloat(pr), am]).sort((a, b) => a[0] - b[0]);
Object.entries(bids).map(([pr, am]) => [parseFloat(pr), am]).sort((a, b) => b[0] - a[0]);
Hope this helps.

What is the best way to clean up the code?

I have a simple function that has to give a change. Something like vending machine. It takes 2 arguments: price of the item and an array of bills and coins received. The output must be an array of numbers only in [quarter, dime, nickel, penny] format. For example, item costs 3.29 and the amount received is [1,1,2]. In this case the output must be [2,2,0,1] because the change which is 0.71 can be divided as 2 quarters, 2 dimes, 0 nickels and 1 penny. If amount received is less than a price then it has to return the full amount but only in format mentioned above. For example, if the price is 5 but amount paid is [2,2], the output must be [16,0,0,0]. I have created this function:
`function change(price,paid) {
const totalPaidVal = paid.reduce((a,b)=>a+b,0)
if (totalPaidVal === price) {
return(Array(4).fill(0))
} else if (price > totalPaidVal) {
const qNum = Math.floor(totalPaidVal/0.25);
const dNum = Math.floor((totalPaidVal-(qNum*0.25))/0.1);
const nNum = Math.floor((totalPaidVal-(qNum*0.25)-(dNum*0.1))/0.05);
const pNum = Math.round((totalPaidVal-(qNum*0.25)-(dNum*0.1)-(nNum*0.05))/0.01);
const arr = [qNum,dNum,nNum,pNum];
return arr;
} else if(price<totalPaidVal) {
const change = totalPaidVal-price;
const qNum = Math.floor(change/0.25);
const dNum = Math.floor((change-(qNum*0.25))/0.1);
const nNum = Math.floor((change-(qNum*0.25)-(dNum*0.1))/0.05);
const pNum = Math.round((change-(qNum*0.25)-(dNum*0.1)-(nNum*0.05))/0.01);
const arr = [qNum,dNum,nNum,pNum];
return arr;
}
}`
It works fine but I know that it looks like I repeat myself over again what is not good. I've been trying to create other variables to clean it up but it still looks pretty weird. There must be more efficient way to do that without repeating same pieces of code all the time. So, here is the question: what is the best way to clean it up and make it look simpler than now?
You can use a function to remove the duplicity from code.
function getNums(value){
const qNum = Math.floor(value/0.25);
const dNum = Math.floor((value-(qNum*0.25))/0.1);
const nNum = Math.floor((value-(qNum*0.25)-(dNum*0.1))/0.05);
const pNum = Math.round((value-(qNum*0.25)-(dNum*0.1)-(nNum*0.05))/0.01);
return [qNum,dNum,nNum,pNum];
}
call this function based on condition
if (price > totalPaidVal) {
return getNums(totalPaidVal)
} else if(price < totalPaidVal){
return getNums(totalPaidVal - price)
}

I need my array to return and array back to another variable while also including its previous array members, no idea how to go about it

This is the test code that it's supposed to pass
function makeArray() {
const array = [];
const t = 10;
for (let i = 0; i < t; i++) {
array.push("I am a strange loop.");
}
return [array, t];
}
describe('loops', () => {
jsdom({
src: fs.readFileSync(path.resolve(__dirname, '..', 'loops.js'), 'utf-8'),
});
describe('forLoop(array)', () => {
it('adds `"I am ${i} strange loop${i === 0 ? \'\' : \'s\'}."` to an array 25 times', () => {
const [array, t] = makeArray();
const strangeArray = forLoop(array);
const testArray = strangeArray.slice(array.length);
const first = "I am 1 strange loop.";
const rest = "I am 24 strange loops.";
expect(strangeArray[11]).to.equal(first);
expect(strangeArray[34]).to.equal(rest);
expect(strangeArray.length).to.equal(t + 25);
});
});
});
this is my code to return the function to strangeArray what I am thinking is that 35 is the total number of members in the array and as the test pass requires me to have 'expect(strangeArray[11]).to.equal(first)' 11th value to be equal to my function return as
"I am 1 strange loop."
function forLoop(array) {
for (let i = 0; i < 35; i++) {
if (array[i] === "I am a strange loop.") {
return;
}
else {
array.push("I am ${i} strange loops.");
}
}
return [array,i];
}
Not sure what you mean exactly but I guess you just want the test to pass? The problem is that the first loop has 'loop' as singular and your indexes don't work either since they would start at 11. That's why your code doesn't work. You can just push to the original array.
function forLoop(array){
for(let i = 0; i < 25; i++){
array.push(`I am ${i} strange loop${i > 1 ? '' : 's'}.`)
}
return array
}

Categories