I need a program that can take input from a user with data quantities and length (feet and inches or just inches) and group the items into groups of 40.
I originally started trying to accomplish this in Excel, but I'm not sure it can be done.
var cutList = [
{ qty: 36, feet: 28, inch: 3 },
{ qty: 6, feet: 27, inch: 8 },
{ qty: 12, feet: 27, inch: 3 },
{ qty: 6, feet: 25, inch: 8 },
{ qty: 16, feet: 25, inch: 3 },
{ qty: 22, feet: 22, inch: 8 },
{ qty: 12, feet: 12, inch: 3 },
];
Group 1 would have all 36 from the first item plus 4 from the second.
Group 2 would have the remaining 2 items from the second line plus all 12 from the 3rd, and down the line until 40 is hit again. It's been a while since I've done any programming, but I was hoping I could do something like this in JavaScript and embed it in an HTML file for people to use. Ideally they'd be able to copy and paste the data from an Excel spreadsheet into this.
There was an answer posted on Friday that I've been playing with and it seems to work. I don't know if it was deleted or edited but I don't see it now. Here is the code:
var cutList = [ { qty: 36, feet: 28, inch: 3 },
{ qty: 6, feet: 27, inch: 8 },
{ qty: 12, feet: 27, inch: 3 },
{ qty: 6, feet: 25, inch: 8 },
{ qty: 16, feet: 25, inch: 3 },
{ qty: 22, feet: 22, inch: 8 },
{ qty: 12, feet: 12, inch: 3 }],
limit = 40,
used = 0,
result = cutList.reduce((r, { qty, feet, inch }, i) => {
var rest, min;
while (qty) {
if (used === 0) r.push([]);
min = Math.min(limit - used, qty)
r[r.length - 1].push({ qty: min, feet, inch, limit });
qty -= min;
used += min;
if (used === limit) used = 0;
}
return r;
}, []);
console.log(result);
So to whoever posted this on Friday, Thank You.
My interpretation of the requirement follows.
// input:
var cutList = [
{ qty: 36, feet: 28, inch: 3 },
{ qty: 6, feet: 27, inch: 8 },
{ qty: 12, feet: 27, inch: 3 },
{ qty: 6, feet: 25, inch: 8 },
{ qty: 16, feet: 25, inch: 3 },
{ qty: 22, feet: 22, inch: 8 },
{ qty: 12, feet: 12, inch: 3 },
]
// proposed output:
let manifest = [
{batchName:A, batch: [{feet: 12, inch: 3}, ... to 40]},
{batchName:B, batch: [{}, {}, {}, ... to 40]},
{batchName:C, batch: [{}, {}, {}, ... to 40]}
]
If the above datagram accurately reflects what you're looking for then the pseudocode can be completed, as both precursor to writing test cases and the algorithm.
// pseudo:
.get line {cutListItem}, sequentially
.set {runningTotal}
.write new {batchName} to {manifest}
do while
.test {cutListItemQty} !== 0
.enumerate decremeent (--) {cutListItemQty} from {cutListItem}
while {runnintTotal} + {qty} in {cutListItem} <= {batchSizeLimit}
do .write to {manifest}
do until either condition:
{cutListItemQty} from {custListItem} === 0
.read {cutList} .get next {cutListItem}
{runningTotal} === {batchSizeLimit}
.init {runningTotal}
.loopback new {batchName}
// considerations
With the sequential method, A chief benefit is being able to stop at any time to then add or change entries which have not yet been processed. Generally, sequential processing offers better overall workflow integration, too, being able to fit asynchronously into a larger process involving the flow of data from creation through various mutations of use. Potential loss is minimized to what remains of the generated list and its progenitor cutlist entries.
At the level of final processing {qty} would be aggregated into batch, and dropped from the datagram, but you might want a reference to the ids of the originating cutlist entries contained in each batch. That speaks to your cutlist, outlining the potential need for identifiers on each entry. We're simply mapping rows and columns here... So, to extend the datagram layout, I'm wondering how you'd view, say, another array entry for each batch, something like: {originsList: [id#3, id#1, id#...], ...}?
I've learned a lot here, so I feel you're in the right place to discover how to put together a working model of your data for a site. And given the programming language internal data type match to your cutlist, from a technical perspective, you appear to be on the right track.
Edit:
The following doesn't hold a candle to what is an artfully concise and complete answer provided by Nina Scholz. Reading her elegant submission again, and comparing it to the original question, I learned my understanding of the problem was limited, as she deeply dives into a min/max solution.
She sets the bar high, and it is a pleasure to read her work. I will continue to learn from that depth of insight as my practical knowledge of Javascript matures in terms of control flow and data structures.
In any case, my code follows:
let cutList = [
{ qty: 36, feet: 28, inch: 3 },
{ qty: 6, feet: 27, inch: 8 },
{ qty: 12, feet: 27, inch: 3 },
{ qty: 6, feet: 25, inch: 8 },
{ qty: 16, feet: 25, inch: 3 },
{ qty: 22, feet: 22, inch: 8 },
{ qty: 12, feet: 12, inch: 3 },
]
// add id field to cutlist retaining this in the array for check against final manifest entries
cutList.filter((item) => item.qty !== 0).forEach((item, idx) => {item.id = idx + 1})
// could expand objects in number of qty to this or new array, keeping original array entries
// Or, decrement the item.qty as each new replica is made with item.subId as an identifier
let qtyTotal = 0
let pooled = []
// console.log(cutList)
// expand cutlist entries by qty to new array, pooled, adding subId to each entry
let arrays = cutList.map((item, idx) => {
// forEach works here, probably better, too - lots of bloatiness going on in either case, trails of replicated lists, not a good production solution
// aggregate entry qty for a total as each entry(item) is parsed
qtyTotal += item.qty
for (let i = 0; i < item.qty; i++) {
let o = {}
o = Object.assign(o, item)
o.subId = i
o.id - i
// console.log(o) // console.dir(o) would be better
pooled.push(o)
}
return pooled
})
// the list manifest will be chunked from
// console.dir(pooled)
// console.log('arrays.length: ', arrays.length)
// make sure the sum of qty matches the number
let valid
if (qtyTotal === pooled.length) {
valid = true
} else {
valid = false
}
console.log(`qty total: ${qtyTotal} pooled total: ${pooled.length}
The manifest data is ${valid ? 'ready.' : 'not ready for use.' }`)
// no exit, program continues
console.log("Producing manifest...")
// chunk pool by batchsize
let chunkedList = []
function produceManifest(start, batchsize) {
let i = start
let k = 0
do {
let qty, feet, inch, id, subId
let x = []
let j = 0
do {
// assign objects from the pool into batchsized arrays
// console.dir(pooled[i]);
// if (typeof pooled[i] === undefined) break;
try {
({qty, feet, inch, id, subId} = pooled[i])
i ++
} catch(e) {
// console.error(e)
// break
}
x[j] = {qty, feet, inch, id, subId}
// console.log(x[j])
j ++
if (i >= pooled.length) break;
// console.log("getting j: " + j)
} while (j <= batchsize - 1) // index in reference array starts at 0
chunkedList[k] = [...x]
// chunkedList.push[x]
// console.dir(chunkedList)
k ++
} while (i <= pooled.length - 1)
// console.dir(chunkedList)
return chunkedList
}
// manifest is an array containing batches of the number requested
// do the job
let start = 0
let batchsize = 40
let printable = produceManifest(start, batchsize)
console.dir(printable)
console.log('debug')
Related
/*
Write each function according to the instructions.
When a function's parameters reference `cart`, it references an object that looks like the one that follows.
{
"Gold Round Sunglasses": { quantity: 1, priceInCents: 1000 },
"Pink Bucket Hat": { quantity: 2, priceInCents: 1260 }
}
*/
function calculateCartTotal(cart) {
let total = 0;
for (const item in cart){
let quantity = Object.values(cart[item])[0];
let price = Object.values(cart[item])[1];
total += price * quantity;
}
return total;
}
function printCartInventory(cart) {
let inventory = "";
for (const item in cart){
inventory += `${Object.values(cart[item])[0]}x${item}\n`;
}
return inventory;
}
module.exports = {
calculateCartTotal,
printCartInventory,
};
The part that confuses me is the function calculateCartTotal. What I am confused about is how does this loop know to grab priceInCents? for example, if I was to add another value into the object called "weight: 24" assuming that it is 24 grams, how does the object value skip over quantity and weight and just grab priceInCents? Hopefully I am making sense on how I am confused and that someone has an explanation for me!
If you try to run below program then it will be easier for you to visualize everything.
What is happening is item is just the index of element and for an object we can either use the key name to access its value or its index.
You can read this doc to understand what Object.values() does.
function calculateCartTotal(cart) {
let total = 0;
for (const item in cart) {
console.log(item)
let quantity = Object.values(cart[item])[0];
let price = Object.values(cart[item])[1];
total += price * quantity;
}
return total;
}
var cart = [
{
quantity: 2,
price: 5,
weight: 24
},
{
quantity: 3,
price: 10,
weight: 90
},
{
quantity: 7,
price: 20,
weight: 45
},
{
quantity: 1,
price: 100,
weight: 67
}
]
console.log(calculateCartTotal(cart))
OUTPUT:
0
1
2
3
280
Program 2 to demonstrate what is happening
function calculateCartTotal(cart) {
console.log(Object.values(cart[2])[1])
console.log(cart[2]['price'])
console.log(cart[2].price)
}
var cart = [
{
quantity: 2,
price: 5,
weight: 24
},
{
quantity: 3,
price: 10,
weight: 90
},
{
quantity: 7,
price: 20,
weight: 45
},
{
quantity: 1,
price: 100,
weight: 67
}
]
calculateCartTotal(cart)
OUTPUT:
20
20
20
I am Deepak,🙂
I can explain (program 2 demonstration). See my friend,you will be able to see the first console.log i.e, console.log(Object.values(cart[2])[1])...
So, object means the whole cart and .values means the only numbers that a particular object contained. Now, see the result i.e, 20.
So, how this 20 will came?...
Now, see the console.log that I have written before. In the brackets of .value cart of [2](it means that 2 is the position of that cart, that why it is written as cart[2] i.e, inside a cart 2nd position's object and after cart[2] this one number is there [1], it means inside the 2nd position's object i.e,
OBJECT below:- position of an objects
var cart = [
quantity: 2, 0 position
price: 5,
weight: 24
},
{
quantity: 3, 1 position
price: 10,
weight: 90
},
{
quantity: 7, 2 position
price: 20,
weight: 45
} ,
{
quantity: 1, 3 position
price: 100,
weight: 67
}
]
console.log(calculateCartTotal(cart))
Now, match the console.log.
it says that console.log(OBJECT.values(cart[2])[1]);
In the cart, see the 2nd position's object i.e,
{
quantity: 7, 2 position
price: 20,
weight: 45
}
So, cart[2] means the whole object you will above.
[1] means inside the object count the position from 0 onwards. So, in the
values
0 position quantity, 7,
1 position price, 20,
2 position weight. 45.
In the [1] position price: 20.
So, cart[2] means
{
quantity: 7, 2 position
price: 20,
weight: 45
}
And,
[1] means price: 20.
So, your answer is 20.
Note: the numbers that is inside the square brackets will gives the position of an object or inside an object.
I've got an example array that I'm trying to reduce by the counts of the occurrence of a key (sentiment in this example):
const sentimentLog = [
{
id: 1,
createdOn: new Date('2020-02-13'),
sentiment: 1
},
{
id: 2,
createdOn: new Date('2020-02-12'),
sentiment: 1
},
{
id: 3,
createdOn: new Date('2020-02-12'),
sentiment: 2
},
{
id: 4,
createdOn: new Date('2020-02-11'),
sentiment: 3
},
{
id: 5,
createdOn: new Date('2020-02-11'),
sentiment: 2
},
{
id: 6,
createdOn: new Date('2020-02-10'),
sentiment: 1
},
{
id: 7,
createdOn: new Date('2020-02-10'),
sentiment: 2
},
{
id: 8,
createdOn: new Date('2020-02-09'),
sentiment: 1
}
]
I'm using:
const sentimentGrouped = (sentiments) => {
return sentiments.reduce((hash, { sentiment }) => {
hash[sentiment] = (hash[sentiment] || 0) + 1
return hash
}, [])
}
And it's nearly there. What I can't figure out is how to replace undefined when there's no sentiment scores of 0 (which is a possibility).
console.log('sentimentGrouped', sentimentGrouped(sentimentLog))
The above produces:
"sentimentGrouped" [undefined, 4, 3, 1]
Whereas I'd like:
"sentimentGrouped" [0, 4, 3, 1]
What am I missing?
Thanks in advance.
Edit: I'll elaborate a bit further, there's 4 scores that will be returned (0 to 3). The data returned will be based on a date range. So there may be instances where there'll be no 1s returned, similarly no 3s returned by a different date range.
The issue is that if you never touch an element of the array, then it stays as a hole in the array, which means it's treated as undefined. Since you know the length of the array i would just prefill the array with zeros. Any sentiment score that does occur will be incremented. Any one that doesn't will stay with its initial value.
return sentiments.reduce((hash, { sentiment }) => {
hash[sentiment] = hash[sentiment] + 1
return hash
}, [0, 0, 0, 0])
I have a score array containing two objects: Liga and Premier. These 2 objects are an array of a list of teams.
I was able to define the greater string when score was previously a single array of objects.
This is the demo i have reproduced where the comparison works fine.
This is the code calculating the higher value comparing the 2 objects.
const maxAverage = teams => {
return teams.map(team => {
return {
team:team,
avg: getAverage(team)
}
}).reduce((a,b)=>a.avg>b.avg?a:b).team
}
<p>Stronger Team:{maxAverage([this.state.homeCity,this.state.awayCity])</p>
The problem now is that now score is an array of the 2 object as i said and i am trying to change my function in something like
const maxAverage = (league, teams) => {
return teams.map(team => {
return {
team:team,
avg: getAverage(league,team)
}
}).reduce((a,b)=>a.avg>b.avg?a:b).team
}
I am not able to pass to my function maxAverage the parameter of one of the two leagues selected and then the 2 objects ( teams ) i want to compare.
i want to do something like this:
<p>Stronger Team:{maxAverage([this.state.selectedLeague], this.state.selectedHomeTeam,this.state.selectedAwayTeam])}
This is the other demo i have reproduced with the current situation.
Given the signature const maxAverage = (league, teams) => ..., following code would match the expected arguments (not sure about the business logic though):
maxAverage(
this.state.selectedLeague,
[this.state.selectedHomeTeam, this.state.selectedAwayTeam]
)
I looked at your second demo and I think you have two choices to get the correct team selected and you can reuse your previous getAverage method for both. Either
const maxAverage = (league, teams) => {
const currentLeague = [scores][0][league]
return teams
.map(team => {
return {
team: team,
avg: getAverage(currentLeague, team)
};
})
.reduce((a, b) => (a.avg > b.avg ? a : b)).team;
};
alternatively you could keep the original maxAverage code and change how you implement the league value eg.
<p>
Stronger Team:
{maxAverage(scores[this.state.selectedLeague], [
this.state.selectedHomeTeam,
this.state.selectedAwayTeam
])}
</p>
Why not simply extract team when selected, save in the state and use the same method used before?
What is a problem?
const scores = {'liga':[
{ day: "1", Barcelona: 1, Real: 3, Valencia: 0 },
{ day: "2", Barcelona: 4, Real: 6, Valencia: 3 },
{ day: "3", Barcelona: 7, Real: 7, Valencia: 3 },
{ day: "4", Barcelona: 7, Real: 8, Valencia: 6 }
], 'primier':[
{ day: "1", Barcelona: 1, Real: 3, Valencia: 0 },
{ day: "2", Barcelona: 4, Real: 6, Valencia: 3 },
{ day: "3", Barcelona: 7, Real: 7, Valencia: 3 },
{ day: "4", Barcelona: 7, Real: 8, Valencia: 6 }]};
const getAverage = (type, team) => {
if (isNaN(scores[type][0][team])) return null;
return scores[type].map(x => x[team]).reduce((a, c) => a + c) / scores[type].length;
};
getAverage('liga',this.state.homeCity);
src:
https://codesandbox.io/s/recharts-examples-d9qy0
Im trying to get the user & value with the highest number from this array but have had no luck in my searches. I'm starting to wonder if my array is poorly written.
{
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
}
const users = {
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
};
const highestUser = users => Object.keys(users).reduce(
(highest, current) => highest.val > users[current] ? highest : { user: current, val: users[current] },
{ user: undefined, val: -Infinity }
).user;
console.log(highestUser(users));
Use keys() and entries() methods to search your JSON object. Save largest value into e.g. const largest and then find out which key belongs to this value.
Let me try to squeeze it into a one-liner approach using Object.keys() and Array.reduce().
const users = {
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
};
const res = Object.keys(users).reduce((a, b) => users[a] > users[b] ? a : b);
console.log(res);
How the above code works is that I get the array of keys from the users object, and I use reduce to get the highest possible value and return the corresponding property from the array obtained from Object.keys().
What you show in your question is an Object, not an Array; however, it does need to be turned into an array in order to work with it.
You can do that with Object.entries(), which will return an array of all the key/value pairs in the object.
Then you can use Array.reduce() to extract the one with the largest value.
const data = {
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
}
let winner = Object.entries(data).reduce((a, b) => (a[1] > b[1]) ? a : b)
console.log(winner)
I need to find ideal pairing amongst tournament players based on following rules:
players with equal points score or similar should be matched
two players can have only one mutual match in tournament
all players must have a match in a round
Its basically a simplified Swiss tournament system.
I have followings standings:
[{
"playerIndex": 0,
"points": 0,
"opponents": [3, 2, 4]
}, {
"playerIndex": 1,
"points": 3,
"opponents": [4, 5, 2]
}, {
"playerIndex": 2,
"points": 3,
"opponents": [5, 0, 1]
}, {
"playerIndex": 3,
"points": 4,
"opponents": [0, 4, 5]
}, {
"playerIndex": 4,
"points": 6,
"opponents": [1, 3, 0]
}, {
"playerIndex": 5,
"points": 2,
"opponents": [2, 1, 3]
}]
Read as: first array item is player number (index) 0 that already played with players number (index) 3, 2 and 4 and gained 0 points, each item for one of six players in a tournament.
And I need to pick three matches for the fourth match. Following a rule that no two players can play a mutual match more than once I choose from following matches:
[ [ 0, 1 ], [ 0, 5 ], [ 1, 3 ], [ 2, 3 ], [ 2, 4 ], [ 4, 5 ] ]
Each of these six possible matches has a points difference between the two players as follows:
[3, 2, 1, 1, 2, 4]
So ideal pairing for the fourth round that gives each player a match in a round with lowest points difference between paired players is:
[ [0, 5], [1, 3], [2, 4] ]
Is there any way of finding these ideal pairings in real time? It is impossible to try all the possible combinations, because there can be more than 100 people in a tournament and the calculations would take forever.
I have been advised to use https://en.wikipedia.org/wiki/Edmonds%27_algorithm or https://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm (both available in JS: https://www.npmjs.com/package/edmonds-blossom and https://github.com/sfentress/edmunds-karp). But I am not sure how to read the results.
Can somebody help please?
Edit: Hungarian algorithm fails if there is too many possible solutions. In my case after first round when there is a lot of players with same amount of points.
Edmond Blossoms algorithm performs much better (found this JS implementation available via NPM https://github.com/mattkrick/EdmondsBlossom).
Just had trouble understanding how to use it. The main difference is that you need to feed it with pairs and the points difference between pairs is higher for the pairs that should be preferred. So I use zero difference for pairs that already played before.
My final (hopefully) solution:
var maxDiff = (roundIndex + 1) * this.config.pointsWin
var possiblePairs = []
availablePlayers.forEach(player => {
availablePlayers.forEach(opponent => {
if (
player.playerIndex !== opponent.playerIndex // &&
// player.opponents.indexOf(opponent.playerIndex) === -1
) {
var match = [player.playerIndex, opponent.playerIndex]
match.sort(function(a, b) {
return a - b;
})
if (player.opponents.indexOf(opponent.playerIndex) === -1) {
match.push(maxDiff - Math.abs(player.points - opponent.points))
}
else {
match.push(0)
}
if (this.searchForArray(possiblePairs, match) === -1) {
possiblePairs.push(match)
}
}
})
})
var rawPairing = edmondsBlossom(possiblePairs)
rawPairing.forEach((match, index) => {
if (match !== -1 && match < index) {
round.matches.push({
home: match,
home_score: '',
away: index,
away_score: '',
referee: -1
})
}
})
First I count max possible points difference amongst players (expecting someone could gain zero points and someone else all of them). Then create all possible combinations between players and mark them with MAX POSSIBLE POINTS - PLAYERS POINTS DIFFERENCE or ZERO for players that matched before.
Then feed the array to EdmondsBlossom function that returns array of integers...
[6,8,14,5,15,3,0,10,1,12,7,13,9,11,2,4]
...read as follows: player index 0 should be paired with player 6, player 1 vs 8, player 2 vs 14, player 3 vs 5... player 5 vs 3 (duplicates). Sometimes there is -1 value in the output that is simply skipped.
Here is my solution (deprecated):
Thanks to #VedPrakash's comment I found the Hungarian algorithm that solves my problem. Luckily there is also a JS implementation available on NPM https://github.com/addaleax/munkres-js.
The Munkers function needs a matrix as input. In my case it is players points difference on intersections of my matrix (see below). The pairs that already played each other have higher value that cant be achieved (9 in my case).
Input matrix:
[ [ 9, 4, 9, 9, 9, 3 ],
[ 4, 9, 9, 2, 9, 9 ],
[ 9, 9, 9, 2, 4, 9 ],
[ 9, 2, 2, 9, 9, 9 ],
[ 9, 9, 4, 9, 9, 5 ],
[ 3, 9, 9, 9, 5, 9 ] ]
Output:
[ [ 0, 5 ], [ 1, 3 ], [ 2, 4 ], [ 3, 1 ], [ 4, 2 ], [ 5, 0 ] ]
The last thing to take care of is filter the Munkers output (that contains duplicates - both pairs 0vs1 and 1vs0) so i filter them simply by comparing first and second index.
My implementation:
var maxDiff = (roundIndex + 1) * this.config.pointsWin
// prepare matrix
var matrix = [];
for (var i = 0; i < availablePlayers.length; i++) {
matrix[i] = new Array(availablePlayers.length);
matrix[i].fill(0)
}
// fill matrix with players points diff
for (var y = 0; y < availablePlayers.length; y++) {
var playerY = availablePlayers[y]
for (var x = 0; x < availablePlayers.length; x++) {
var playerX = availablePlayers[x]
if (x === y) {
matrix[x][y] = maxDiff
}
else if (playerY.opponents.indexOf(x) !== -1) {
matrix[x][y] = maxDiff
matrix[y][x] = maxDiff
}
else {
var value = Math.abs(playerX.points - playerY.points)
matrix[x][y] = value
matrix[y][x] = value
}
}
}
// console.table(matrix)
// console.table(computeMunkres(matrix))
// return
// make pairing
var rawPairing = computeMunkres(matrix)
rawPairing.forEach(pairing => {
if (pairing[0] < pairing[1]) {
round.matches.push({
home: pairing[0],
home_score: '',
away: pairing[1],
away_score: '',
referee: -1
})
}
})