A counter keeping track of multiple values inside an array - javascript

This seems sort of complex so I'll do my best to be as clear as possible.The particular function I'm looking for dynamically creates a money spent | money won chart for a game of gambling.
I have a lottery of sorts that the user can bet on. There are 6 items the user can buy which each have 6 prizes:
These can be put into objects or arrays.
var prices = [5,10,28,50,56,280].
var possibleWins = [40,80,250,400,500,2500]
I'm trying to create a chart that calculates how much money you would have to spend on each particular item per game to guarantee you gain money - for 300 games out.
So here is an example of how the chart should start off:
investment = max possible winnings + total spent( which is negative )
The 2nd row is assuming the first game already happened and lost. And so on.
The idea is to start with the smallest item but give up once it can no longer get you positive even if you win. This is why on row 9 we switch to the rock. ( our investment is at 0 and if we play a twig again, the most we can win is 40. So even if we did win, we would actually have lost 5 overall. )
Also worth pointing out is that if you win on 1 item; you win on all items for that particular game. So you get all prizes combined.
I've been working on this for a few days now and some of these related questions have my initial attempts ( but I honestly have no idea ):
How to find the lowest possible combination of keys within an array
Counter that generates the lowest sum from a combination of indexes above the previous value
Add an arrays keys to themselves until exceeding a limit?
EDIT: At least 1 item(s) must be bought every game and games cannot be skipped

Basically this proposal relies on a function to get the next items
getItems = function () {
var price = 0,
array = lottery.map(function (a) { return a.price; });
return function () {
var items;
do {
items = combine(array, price);
price++;
} while (!items.length)
return items;
}
}(),
which starts at price with zero and increments the value by one until a combination of items is found. Then the items array is returned. The function works as generator.
The other important function is the combination of items with a given price and the try to get an array with the items.
function combine(array, sum) {
function c(left, right, sum) {
if (!sum) {
result = right;
return true;
}
return left.some(function (a, i, aa) {
return a <= sum && c(aa.slice(i + (a > sum - a)), right.concat(a), sum - a);
});
}
var result = [];
c(array.sort(function (a, b) { return b - a; }), [], sum);
return result;
}
combine takes an array with prices and a wanted sum to reach with combinating the given prices. If successfull, an array with the items is returned, otherwise an empty array.
The third part is to use the items as long as the investment is not negative. If that happens, a new items set is fetched.
function combine(array, sum) {
function c(left, right, sum) {
if (!sum) {
result = right;
return true;
}
return left.some(function (a, i, aa) {
return a <= sum && c(aa.slice(i + (a > sum - a)), right.concat(a), sum - a);
});
}
var result = [];
c(array.sort(function (a, b) { return b - a; }), [], sum);
return result;
}
var lottery = [{ name: 'twig', price: 5, win: 40 }, { name: 'rock', price: 10, win: 80 }, { name: 'shell', price: 28, win: 250 }, { name: 'chip', price: 50, win: 400 }, { name: 'gold', price: 56, win: 500 }, { name: 'diamond', price: 280, win: 2500 }],
lotteryByPrice = lottery.reduce(function (r, a) { r[a.price] = a; return r; }, Object.create(null)),
getItems = function () {
var price = 0,
array = lottery.map(function (a) { return a.price; });
return function () {
var temp;
do {
temp = combine(array, price);
price++;
} while (!temp.length)
return temp;
}
}(),
createTableRow = function (element) {
var table = document.createElement('table'),
tr = document.createElement('tr');
['Game', 'Items', 'Types', 'Spend Per Game', 'Total Spend', 'Max. Possible Winnigs', 'Investment'].forEach(function (a) {
var th = document.createElement('th');
th.appendChild(document.createTextNode(a));
tr.appendChild(th);
});
table.appendChild(tr);
element.appendChild(table);
return function (row) {
var tr = document.createElement('tr');
['game', 'items', 'types', 'spend', 'total', 'potential', 'investment'].forEach(function (k) {
var td = document.createElement('td');
td.appendChild(document.createTextNode(row[k]));
tr.appendChild(td);
});
if (row.topBorder) {
tr.style.borderTop = '2px solid #666';
}
table.appendChild(tr);
};
}(document.body),
row = { game: null, items: null, types: null, spend: null, total: 0, potential: null, investment: null },
i,
items = getItems(),
add = function (a, b) { return a + b; },
winP = function (a) { return lotteryByPrice[a].win; },
nameP = function (a) { return lotteryByPrice[a].name; };
for (i = 1; i <= 70; i++) {
row.topBorder = false;
while (row.total - items.reduce(add) + items.map(winP).reduce(add) < 0) {
items = getItems();
row.topBorder = true;
}
row.game = i;
row.items = items.length;
row.types = items.map(nameP).join(' + ');
row.spend = -items.reduce(add);
row.total += row.spend;
row.potential = items.map(winP).reduce(add);
row.investment = row.potential + row.total;
createTableRow(row);
}
table { border-collapse: collapse; font-family: Sans-Serif; }
th { border: 1px solid #ccc; padding: 0 10px; }
td { text-align: center; border: 1px solid #ccc; }

Here is my solution
let items = [{
name: 'twig',
price: 5,
win: 40
}, {
name: 'rock',
price: 10,
win: 80
}, {
name: 'shell',
price: 28,
win: 250
}, {
name: 'chip',
price: 50,
win: 400
}, {
name: 'gold',
price: 56,
win: 500
}, {
name: 'diamond',
price: 280,
win: 2500
}];
let moves = [];
Move.prototype.numberItems = function() {
let count = 0;
for (let n = 0; n < 6; n++) {
count += this.counts[n];
}
return count;
}
Move.prototype.nameItems = function() {
let name = '';
for (let n = 0; n < 6; n++) {
for (let x = 0; x < this.counts[n]; x++) {
if (name != '') {
name += ' - ';
}
name += items[n].name;
}
}
return name;
}
Move.prototype.getWin = function() {
let win = 0;
for (let n = 0; n < 6; n++) {
win += this.counts[n] * items[n].win;
}
return win;
}
function Move(cost, counts) {
this.cost = cost;
this.counts = counts.slice();
}
function run() {
createMoves(100);
moves.sort(function(a, b) {
return (a.cost - b.cost);
});
print();
}
function createMoves(maxCost) {
let counts = [];
for (let n = 0; n < 6; n++) {
counts.push(0);
}
counts[0] ++;
while (true) {
let cost = whatCost(counts);
if (cost < maxCost) {
moves.push(new Move(cost, counts));
counts[0] ++;
continue;
}
if (!escalate(counts)) {
break;
}
}
}
function whatCost(counts) {
let cost = 0;
for (let n = 0; n < 6; n++) {
cost += counts[n] * items[n].price;
}
return cost;
}
function escalate(counts) {
for (let n = 0; n < 5; n++) {
if (counts[n] != 0) {
counts[n] = 0;
counts[n + 1] ++;
return true;
}
}
return false;
}
function print() {
let domResult = document.getElementById('results');
let game = 1;
let moveInx = 0;
let spent = 0;
for (let moveInx = 0; moveInx < moves.length; moveInx++) {
let myMove = moves[moveInx];
let items = myMove.numberItems();
let win = myMove.getWin();
let cost = myMove.cost;
for (let repeat = 1;; repeat++) {
let investment = win - spent - cost;
if (investment < 0) {
break;
}
spent += cost;
let row = document.createElement('tr');
if (repeat == 1) {
row.className = 'first';
}
let cell = document.createElement('td');
cell.innerHTML = game;
row.appendChild(cell);
cell = document.createElement('td');
cell.innerHTML = items;
row.appendChild(cell);
cell = document.createElement('td');
cell.innerHTML = myMove.nameItems();
row.appendChild(cell);
cell = document.createElement('td');
cell.innerHTML = cost;
row.appendChild(cell);
cell = document.createElement('td');
cell.innerHTML = spent;
row.appendChild(cell);
cell = document.createElement('td');
cell.innerHTML = win;
row.appendChild(cell);
cell = document.createElement('td');
cell.innerHTML = win - spent;
row.appendChild(cell);
domResult.appendChild(row);
game++;
if (game > 300) {
return;
}
}
}
}
table {
border-collapse: collapse;
}
tr * {
border: solid 1px black;
}
.first {
border-top: solid 4px blue;
}
<button onclick="run()">Run</button>
<table>
<thead>
<tr>
<th>Game</th>
<th>Items</th>
<th>Types</th>
<th>Spent</th>
<th>Total Spent</th>
<th>Max win</th>
<th>Profit</th>
</tr>
</thead>
<tbody id="results">
</tbody>
</table>

You can create an object where property names are set the values of possibleWins. Set all of the possible combinations of investing the limit at each round. The arrays do not contain all possible combinations of numbers less than the limit for that particular round. That is, the numbers are not dispersed in every possible combination. For example, at round 40, [10, 10, 10, 10, 0, 0, 0, 0] is included as an array; though the array could also be rearranged to [10, 0, 10, 10, 0, 10, 0, 10], or other combination of indexes totaling less than 40.
Additional of the possible allowed combinations less than the limit for that round be pushed to the array corresponding a specific round at the returned object.
This implementation does not attempt to locate the selection routes of each round which would lead to a positive outcome. The entire set of arrays can be iterated as to each matching index in each array, combination of random indexes, or every possible combination of indexes.
The approach is a base template from which possible selections can be made. Further optional arrays containing combinations of values less object property name, that is the particular round, or values within arrays from within arrays at properties of object having a property name value less than the current round, can be added to the array of arrays; to find the combinations of selections which lead to the expected outcome.
const [prices, possibleWins] = [
[5, 10, 28, 50, 56, 280],
[40, 80, 250, 400, 500, 2500]
];
const counteropts = (prices, possibleWins) => {
let rounds = {};
for (let price of prices) {
let [chance, limit] = [[], possibleWins[prices.indexOf(price)]];
for (let buyin = price - price; buyin <= limit; buyin += price) {
chance[chance.length] = buyin;
}
if (chance[chance.length - 1] !== limit) {
chance = [...chance, limit]
}
for (let odd of Array.of(chance)) {
let options = Array();
for (let choice of odd) {
options[options.length] = [...odd.map(
v => v !== choice && v + choice <= limit ? v + choice : 0
)];
if (options.length === prices.length -1) {
for (let option of options[0]) {
let keys = options[0].map((_, index) => index + 1)
.filter(key => key * option <= limit);
let opt = Array(keys.length).fill(option);
options = [...options
, opt.length < options[0].length
? [...opt, ...Array(options[0].length - opt.length).fill(0)]
: opt
];
}
rounds[limit] = [...options];
}
}
}
}
return rounds
}
let opts = counteropts(prices, possibleWins);
console.log(opts);

Related

Javascript: Distribute values into two arrays

Im new here and into programming. Im not native english so please apologize if you dont understand me very well :-)
I`ve started learning Javascript and still doing.
Now I want to achieve a simple project which gives me two Teams with equal skills.
Questions:
It seems to work but I want to get random teams. This gives me always the same. How can I resolve this?
I am able to print the values to see that its correct, but how can I print the player names (Variables like P1, P2, P3...)
Thank you
//Player skill values
let P1 = 2;
let P2 = 3;
let P3 = 1;
let P4 = 3;
let P5 = 4;
let P6 = 4;
let P7 = 5;
let P8 = 2;
//try to achieve two equal teams
function aufteilen(arr) {
arr.sort().reverse()
let a = [], b = [], sumA = 0, sumB = 0, i = 0
while (i < arr.length) {
if (!sumA && !sumB || sumA == sumB) {
a.push(arr[i])
sumA += arr[i]
} else if (sumA < sumB) {
a.push(arr[i])
sumA += arr[i];
} else if (sumB < sumA) {
b.push(arr[i])
sumB += arr[i];
}
i++
}
console.log(`Total: ${sumA} ${sumB}`)
return [a, b]
}
console.log(aufteilen([P1, P2, P3, P4, P5, P6, P7, P8]))
Creating a team selection based on skill and making it random using the OP's algorithm is not really possible.
It's also possible for the OP's code to make teams based on player count not equal, I would assume a team picker would try to make teams equal on players & skill.
So one approach is just a simple brute force one, basically randomly pick teams, and compare the skills. Stop comparing after a set amount of time, just in case a perfectly equal skill cannot be met, or when skills are equal.
Below is a working example doing the above, it will also handle making the teams equal if we don't have an even number of players.
const players = [
{name: 'P1', skill: 2},
{name: 'P2', skill: 3},
{name: 'P3', skill: 1},
{name: 'P4', skill: 3},
{name: 'P5', skill: 4},
{name: 'P6', skill: 4},
{name: 'P7', skill: 5},
{name: 'P8', skill: 2},
];
const score = team =>
team.reduce((a, v) => {
a += v.skill;
return a;
}, 0);
function createFairTeam(players) {
function createTeams() {
const team = {
team1: [],
team2: [],
skillTot1: 0,
skillTot2: 0,
score: 0
}
const p = [...players];
const t1size = Math.trunc(players.length / 2);
//fill team1
for (let l = 0; l < t1size; l++) {
const pos = Math.trunc(Math.random() * p.length);
const f = p.splice(pos, 1)[0];
team.skillTot1 += f.skill;
team.team1.push(f);
}
//fill team2
for (let l = 0; l < p.length;) {
const pos = Math.trunc(Math.random() * p.length);
const f = p.splice(pos, 1)[0];
team.team2.push(f);
team.skillTot2 += f.skill;
}
//calc score.
team.score = Math.abs(
score(team.team1) -
score(team.team2)
);
return team;
}
const team = createTeams();
let best = team;
const start_time = Date.now();
while (Date.now() - start_time < 200) {
//if total skill is odd, the best score
//would be 1, if even it would be 0
//so lets check for less than equal to 1
//for early termination.
if (best.score <= 1) break;
const newTeams = createTeams();
if (newTeams.score < best.score)
best = newTeams;
}
return best;
}
function doIt() {
const teams = createFairTeam(players);
const table = document.querySelector('table');
const tbody = table.querySelector('tbody');
tbody.innerHTML = '';
for (let l = 0;
l < Math.max(teams.team1.length, teams.team2.length); l ++) {
const p1 = teams.team1[l];
const p2 = teams.team2[l];
const tr = document.createElement('tr');
////
let td = document.createElement('td');
td.innerText = p1 ? p1.name : '';
tr.appendChild(td);
////
td = document.createElement('td');
td.innerText = p1 ? p1.skill : '';
tr.appendChild(td);
////
td = document.createElement('td');
td.innerText = p2 ? p2.name : '';
tr.appendChild(td);
tbody.appendChild(tr);
////
td = document.createElement('td');
td.innerText = p2 ? p2.skill : '';
tr.appendChild(td);
tbody.appendChild(tr);
}
//// tots
document.querySelector('#t1total').innerText = teams.skillTot1;
document.querySelector('#t2total').innerText =
teams.skillTot2;
}
doIt();
document.querySelector('button').addEventListener('click', doIt);
table {
width: 100%;
}
<button>Another Team</button>
<table border="1">
<thead>
<tr>
<th colspan="2">Team 1</th>
<th colspan="2">Team 2</th>
</tr>
<tr>
<th>Player</th>
<th>Skill</th>
<th>Player</th>
<th>Skill</th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<td>Total</td><td id="t1total"></td>
<td>Total</td><td id="t2total"></td>
</tr>
</tfoot>
</table>
1 - you need a shuffle method, something like below or from a library:
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
2 - you need an object to display player names. your players are just numbers.
// your player
let P1 = 2;
// what you would need to display a name
let P1 = { id: 2, name: "Mario" }
// then u can display the name with
P1.name // "Mario"
Your array.sort().reverse() is the reason why you get this.
I would recommend to shuffle() before passing it to your function.
Here is my guess :
function shuffle(arr) {
for (let i = arr.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
console.log(aufteilen(shuffle([P1, P2, P3, P4, P5, P6, P7, P8])));
You can then create an object Players, add all infos you want and do it like this :
let players = {
P1: 2,
P2: 3,
P3: 1,
P4: 3,
P5: 4,
P6: 4,
P7: 5,
P8: 2,
};
let playerSkills = Object.values(players);
console.log(aufteilen(shuffle(playerSkills)));
Hope it will help !

javascript least amount of elements from an integer array that can be used to get to a total value

please can somebody help?
If i have a total or a sum for instance 91
How can I create an array of the least amount of elements needed to get to the total value?
[50, 20, 10 , 5, 3, 2, 1] totaling this array will provide 91.
I know how to perform the opposite function using reduce or like so:
<script>
var numbers = [65, 44, 12, 4];
function getSum(total, num) {
return total + num;
}
function myFunction(item) {
document.getElementById("demo").innerHTML = numbers.reduce(getSum);
}
</script>
Greedy algorithm
Here is a solution using greedy algorithm. Note that this solution will work correctly in case when all the smaller numbers are divisors of all the bigger numbers such as in case [50, 10, 5, 1]. (see dynamic algorithm below this one for solution that can handle any input)
50 mod 10 = 0
50 mod 5 = 0
50 mod 1 = 0
10 mod 5 = 0
10 mod 1 = 0
5 mod 1 = 0
const sum = xs => xs.reduce((acc, v) => acc + v, 0);
function pickSubset(options, total, currentPick) {
if (sum(currentPick) === total) { return currentPick; }
if (options.length === 0) { return null; }
const firstVal = options[0];
let res = null;
if (sum(currentPick) + firstVal > total) {
res = pickSubset(options.slice(1), total, currentPick);
} else {
let opt1 = pickSubset(options, total, currentPick.concat(options[0]));
let opt2 = pickSubset(options.slice(1), total, currentPick.concat(options[0]));
if (opt1 && opt2) {
opt1.length < opt2.length ? res = opt1 : res = opt2
} else if (opt1) {
res = opt1;
} else {
res = opt2;
}
}
return res;
}
const total = 73;
const options = [50, 25, 10, 5, 2, 1];
console.log(pickSubset(options, total, []));
To handle unsorted input you can wrap it in another function and sort it prior to passing it to the main function.
const sum = xs => xs.reduce((acc, v) => acc + v, 0);
function pickSubset(options, total, currentPick) {
const sortedOptions = options.sort((a, b) => b - a);
function _pickSubset(options, total, currentPick) {
if (sum(currentPick) === total) { return currentPick; }
if (options.length === 0) { return null; }
const firstVal = options[0];
let res = null;
if (sum(currentPick) + firstVal > total) {
res = pickSubset(options.slice(1), total, currentPick);
} else {
let opt1 = pickSubset(options, total, currentPick.concat(options[0]));
let opt2 = pickSubset(options.slice(1), total, currentPick.concat(options[0]));
if (opt1 && opt2) {
opt1.length < opt2.length ? res = opt1 : res = opt2
} else if (opt1) {
res = opt1;
} else {
res = opt2;
}
}
return res;
}
return _pickSubset(sortedOptions, total, currentPick);
}
const total = 73;
const options = [50, 25, 10, 5, 2, 1].reverse();
console.log(pickSubset(options, total, []));
Dynamic programming (bottom-up natural ordering approach)
This solution works correctly for any type of input.
function pickSubset(options, total) {
function _pickSubset(options, change, minNums, numsUsed) {
for (let i = 0; i < change + 1; i++) {
let count = i;
let newNum = 1;
let arr = options.filter(v => v <= i);
for (let j of arr) {
if (minNums[i - j] + 1 < count) {
count = minNums[i - j] + 1;
newNum = j;
}
}
minNums[i] = count;
numsUsed[i] = newNum;
}
return minNums[change];
}
function printNums(numsUsed, change) {
const res = [];
let num = change;
while (num > 0) {
let thisNum = numsUsed[num];
res.push(thisNum);
num = num - thisNum;
}
return res;
}
const numsUsed = [];
const numsCount = [];
_pickSubset(options, total, numsCount, numsUsed);
return printNums(numsUsed, total);
}
const options = [50, 10, 5, 2, 1];
console.log(pickSubset(options, 73));
Dynamic programming (top-down memoization approach)
// helper function that generates all the possible solutions
// meaning, all the possible ways in which we can pay the provided amount
// and caches those solutions;
// returns the number of possible solutions but that is not neccessary
// in this case
const _pickSubset = (toPay, options, currentPick, cache) => {
if (toPay < 0) { return 0; }
if (toPay === 0) {
cache.add(currentPick);
return 1;
}
if (options.length === 0) { return 0; }
return _pickSubset(toPay - options[0], options, currentPick.concat(options[0]), cache)
+ _pickSubset(toPay, options.slice(1), currentPick, cache);
};
// memoize only with respect to the first two arguments - toPay, bills
// the other two are not necessary in this case
const memoizeFirstTwoArgs = fn => {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args.slice(0, 2));
if (cache.has(key)) { return cache.get(key); }
const res = fn(...args);
cache.set(key, res);
return res;
};
};
// uses memoized version of makeChange and provides cache to that function;
// after cache has been populated, by executing memoized version of makeChange,
// find the option with smallest length and return it
const pickSubset = (toPay, options) => {
const cache = new Set();
const memoizedPickSubset = memoizeFirstTwoArgs(_pickSubset);
memoizedPickSubset(toPay, options, [], cache);
let minLength = Infinity;
let resValues;
for (const value of cache) {
if (value.length < minLength) {
minLength = value.length;
resValues = value;
}
}
return resValues;
}
const options = [50, 25, 10, 5, 2, 1];
const toPay = 73;
console.log(pickSubset(toPay, options));

Sorting an array of object

I wish to sort an array of medals. My first sort returns an array sorted according to the gold medals. I then wish to range those which are having the same gold but silver medals are different (same for bronze). I use the following codes that actually makes me run out of memory. This is my code:
static sort(data) {
let sorted = data.sort((a, b) => b.medal.gold - a.medal.gold);
let next, temp, current;
for (let i = 0; i < sorted.length; i++) {
current = sorted[i].medal;
if (sorted[i+1]) next = sorted[i+1].medal;
if (next) {
if (current.gold === next.gold) {
if (current.silver < next.silver) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
else if (current.silver === next.silver) {
if (current.bronze < next.bronze) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
}
}
}
}
return sorted;
}
You'll want to improve your compare function so it takes care of that requirement:
data.sort((a, b) => (b.medal.gold - a.medal.gold)
|| (b.medal.silver - a.medal.silver)
|| (b.medal.bronze - a.medal.bronze) )
And then you don't need the (endless) for loop at all.
You have to set next to null somewhere, because it keeps the value from the previous iteration and the if(next) is always true. Afterwards the function will always create one more element and add it in the array (sorted[i+1] = sorted[i]) until you run out of memory.
Here is a working example:
var rawData =
[{ id: 1, medal: {gold: 2, silver: 1, bronze: 1}},
{ id: 2, medal: {gold: 2, silver: 1, bronze: 2} },
{ id: 3, medal: {gold: 5, silver: 1, bronze: 4} } ];
function sortData(data) {
let sorted = data.sort((a, b) => b.medal.gold - a.medal.gold);
let next, temp, current;
for (let i = 0; i < sorted.length; i++) {
next = undefined;
current = sorted[i].medal;
if (sorted[i+1]) next = sorted[i+1].medal;
if (next) {
if (current.gold === next.gold) {
if (current.silver < next.silver) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
else if (current.silver === next.silver) {
if (current.bronze < next.bronze) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
}
}
}
}
return sorted;
};
console.log(sortData(rawData))
Please note that in the function you are using medal instead of medals as the data you have provided in one of your comments.

How to reassign a list (in typescript) based on a "rank" object property?

I am trying to make a function to reassign a list based on their rank property.
For example:(my object has other property)
var array=[
{id:1,rank:2},
{id:18,rank:1},
{id:53,rank:3},
{id:3,rank:5},
{id:19,rank:4},//this item
]
This item {id:19,rank:4} is now in 2d position. The array becomes
item= { currentRank: 4; newRank: 2} //see below
array=[
{id:1,rank:3},
{id:18,rank:1},
{id:53,rank:4},
{id:3,rank:5},
{id:19,rank:2},
]
FYI : These items are re-order after a html drag&drop operation.
So I am trying to make a function to re-assign ranks based on the droped item rank.
I know the drop item new rank and its old rank.
So far I have done the following but it is not working for all cases:
public reorderArray(item: { currentRank: string; newRank: string }, array: { id: string, rank: string }[]): { id: string, rank: string } [] {
let arr = array.map(a => Object.assign({}, a)).sort((a, b) => (parseInt(a.rank) - parseInt(b.rank))).slice();
//To avoid to change the reference??
let isOrdered = arr.every((element, index, array) => {
return array[index + 1] ? element.rank + 1 == array[index + 1].rank : true
});
if (isOrdered && arr[0].rank == (1).toString()) {
if (parseInt(item.currentRank) < parseInt(item.newRank)) {
//on descend un élément dans la liste => +1 entre le currentRank et )le newRank
for (let i = parseInt(item.currentRank); i < parseInt(item.newRank); i++) {
arr[i].rank = (parseInt(arr[i].rank) - 1).toString();
}
arr[parseInt(item.currentRank)].rank = (parseInt(item.newRank)).toString();
}
else if (parseInt(item.currentRank) > parseInt(item.newRank)) {
for (let i = parseInt(item.newRank); i < parseInt(item.currentRank); i++) {
arr[i].rank = (parseInt(arr[i].rank) + 1).toString();
}
arr[parseInt(item.currentRank)].rank = (parseInt(item.newRank) + 1).toString();
}
return arr
}
else {
alert("This list is not ordered");
}
}
nb: if array is not properly oredered (rank is 1,3,4...), function doesn't do anything.
You could use an array for splicing and iterate then for the correction of the range.
function changeRank(object) {
ranks.splice(object.newRank - 1, 0, ranks.splice(object.currentRank - 1, 1)[0]);
ranks.forEach(function (a, i) {
a.rank = i + 1;
});
}
var array = [{ id: 1, rank: 2 }, { id: 18, rank: 1 }, { id: 53, rank: 3 }, { id: 3, rank: 5 }, { id: 19, rank: 4 }],
ranks = [];
array.forEach(a => ranks[a.rank - 1] = a);
console.log(array);
changeRank({ currentRank: 4, newRank: 2 });
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think you might be approaching this incorrectly.
Why not loop through all of the items and then if the rank is equal too or great then the current one increase it's rank? Then once you're done set the rank for the updated item:
Something like this:
for(var x = 0; x < items.length; x++){
if(items[x].rank >= item.newRank && items[x].rank <= item.currentRank){
items[x].rank++;
}
}
item.rank = item.newRank;
This logic must work. I've done it with the concept of array. Consider array index as rank.
if (new_rank < current_rank)
{
item = arr[current_rank]
i = new_rank;
temp = arr[i];
i++;
while(i<current_rank)
{
temp1 = arr[i];
arr[i] = temp;
temp = temp1;
i++;
}
arr[new_rank] = item;
}
else
{
item = arr[current_rank]
i = new_rank;
temp = arr[i];
i--;
while(i>current_rank)
{
temp1 = arr[i];
arr[i] = temp;
temp = temp1;
i--;
}
arr[new_rank] = item;
}

I'm having trouble displaying my randomly selected object

I am having trouble displaying the random object and the properties of that random object. The goal of this project is to have a list of stockItems, and when I press a button, it selects a determined number of those objects and displays them in an HTML p tag. Right now when I try to display it, it prints out as [object]. The goal is to have the properties of the selected object on different lines.
Here is the code I am working with:
function buildShopItems(count) {
var shopItems = [], i, itemIndex;
count = stockItems.length < count ? stockItems.length : count;
function getUniqueRandomItem() { //from stock
var item;
while (true) {
item = stockItems[Math.floor(Math.random() * stockItems.length)];
if (shopItems.indexOf(item) < 0) return item;
}
}
for (i = 0; i < count; i++) {
shopItems.push(getUniqueRandomItem());
}
return shopItems;
console.log(shopItems);
}
var stockItems = [
{ item: "sword", type: "weapon", weight: "5 lbs.", cost: "10 gold" },
{ item: "hammer", type: "weapon", weight: "8 lbs.", cost: "7 gold" }
//...
];
var shopItems = buildShopItems(1);
console.log(shopItems);
document.getElementById("item").innerHTML = shopItems.item;
document.getElementById("type").innerHTML = shopItems.type;
document.getElementById("weight").innerHTML = shopItems.weight;
document.getElementById("cost").innerHTML = shopItems.cost;
The problem was with your usage of indexOf. You can use indexOf to search for an object because in javascript you can't compare object using == or === and indexOf uses ===. Also made some syntax updates for you.
'use strict'
const stockItems = [
{ item: "sword", type: "weapon", weight: "5 lbs.", cost: "10 gold" },
{ item: "hammer", type: "weapon", weight: "8 lbs.", cost: "7 gold" }
];
function isEquivalent(a, b) {
// Create arrays of property names
const aProps = Object.getOwnPropertyNames(a);
const bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (let i = 0; i < aProps.length; i++) {
const propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
// normal indexof will not work with object because it uses strict equality
function myIndexOf(array, object) {
for (let i = 0; i < array.length; i++) {
if (isEquivalent(array[i], object)) return i;
}
return -1;
}
function getUniqueRandomItem(shopItems) { //from stock
var item;
while (true) {
item = stockItems[Math.floor(Math.random() * stockItems.length)];
if (myIndexOf(shopItems, item) < 0) return item;
}
}
function buildShopItems(count) {
count = stockItems.length < count ? stockItems.length : count;
const shopItems = [];
for (let i = 0; i < count; i++) {
const item = getUniqueRandomItem(shopItems);
shopItems.push(item);
}
return shopItems;
}
const shopItems = buildShopItems(1);
console.log(shopItems);

Categories