How to create two arrays in the same loop with CoffeeScript? - javascript

I want to create two arrays b and c at the same time. I know two methods which can be able to achieve it. The first method is
b = ([i, i * 2] for i in [0..10])
c = ([i, i * 3] for i in [0..10])
alert "b=#{b}"
alert "c=#{c}"
This method is very handy for creating only one array. I can not be the better way to get the better performance for computation.
The second method is
b = []
c = []
for i in [0..10]
b.push [i, i*2]
c.push [i, i*3]
alert "b=#{b}"
alert "c=#{c}"
This method seems good for computation efficiency but two lines
b = []
c = []
have to be written first. I don't want to write this 2 lines but I have not find a good idea to have the answer. Without the initialization for the arrays of b and c, we can not use push method.
There exists the existential operator ? in Coffeescript but I don't know hot to use it in this problem. Do you have a better method for creating the arrays of b and c without the explicit initialization?
Thank you!

You can use a little help from underscore (or any other lib that provides zip-like functionality):
[b, c] = _.zip ([[i, i * 2], [i, i * 3]] for i in [0..10])...
After executing it we have:
coffee> b
[ [ 0, 0 ],
[ 1, 2 ],
[ 2, 4 ],
[ 3, 6 ],
[ 4, 8 ],
[ 5, 10 ],
[ 6, 12 ],
[ 7, 14 ],
[ 8, 16 ],
[ 9, 18 ],
[ 10, 20 ] ]
coffee> c
[ [ 0, 0 ],
[ 1, 3 ],
[ 2, 6 ],
[ 3, 9 ],
[ 4, 12 ],
[ 5, 15 ],
[ 6, 18 ],
[ 7, 21 ],
[ 8, 24 ],
[ 9, 27 ],
[ 10, 30 ] ]
See the section about splats in CoffeeScript docs for more details and examples.

How about this using the existential operator:
for i in [0..10]
b = [] if not b?.push [i, i*2]
c = [] if not c?.push [i, i*3]
console.log "b=#{b}"
console.log "c=#{c}"
Or to be a bit more understandable:
for i in [0..10]
(if b? then b else b = []).push [i, i*2]
(if c? then c else c = []).push [i, i*3]
console.log "b=#{b}"
console.log "c=#{c}"
EDIT: from comments:
OK but you you have to write so many tedious codes. The same reason is
also for ` (b = b or []).push [i, i*2]
It is tedious, so we can wrap it in a function (but beware the variables will be global now):
# for node.js
array = (name) -> global[name] = global[name] or []
# for the browser
array = (name) -> window[name] = window[name] or []
for i in [0..10]
array('b').push [i, i*2]
array('c').push [i, i*3]
console.log "b=#{b}"
console.log "c=#{c}"

Related

Common Denominator of an Array of Arrays

I'm trying to migrate a Python code to Javascript and I'm facing same issues on my calculations.
I also have no documentation to base on except this code:
var commonDenominator = ((myClassMap map (sub) -> sub reduce ((item, denominator = 0) -> item * perClassMultiplier[$$])) reduce ($$+$)) as Number
From what I understand, it's trying to read the myClassMap and trying to find the common denominator of everything.
I'm trying to convert but my code is doing a greatCommonDenominator and my calculations are wrong in the order of 100, I believe. Maybe I'm wrong...
Entry:
myClassMap -> [ [ 1, 150 ], [ 1, 0 ], [ 1.5, 8 ], [ 1.5, 0 ] ]
perClassMultiplier -> [ 1, 1, 1.5, 1.5 ]
Code
myClassMap.map(entry => {
const commonDenominator = greaterCommonDenominator(entry[0], entry[1]);
if(data.commonDenominator == null | commonDenominator > data.commonDenominator) {
data.commonDenominator = commonDenominator;
}
});
const greaterCommonDenominator = function(a, b) {
if (!b) {
return a;
}
return greaterCommonDenominator(b, a % b);
}
Any help? I want to make sure the code does the same calculation

How do you push a variable into an array multiple times without it changing?

I'm trying to push some values in an array for something called "Brain.js". When storing a variable in the array and later changing it, all the variables that were stored in the array change. Can someone help me make it so they don't change? I'm having much trouble with this.
Here's an example:
var hold = ([
]);
var a = [1, 1, 1]
var b = [2];
hold.push(
{ input: a, output: b }
);
console.log(hold); // returns [ { input: [ 1, 1, 1 ], output: [ 2 ] } ]
a[2] = 2;
b = [3];
hold.push(
{ input: a, output: b }
);
console.log(hold);
// Expected output: [ { input: [ 1, 1, 1 ], output: [ 2 ] }, { input: [ 1, 1, 2 ], output: [ 3 ] } ]
// What it really returns: [ { input: [ 1, 1, 2 ], output: [ 2 ] }, { input: [ 1, 1, 2 ], output: [ 3 ] } ]
Problem is, that you are not pushing actual number into the array, but reference. In other words, you passed twice the reference to same object.
What could you do, is create a copy of the object when you are passing it to hold. You can use eg. spread operator.
hold.push(
{
input: ...a,
output: ...b
}
);
You can find out more here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
Problem is you are updating an existing array a which is already referenced inside first object you pushed. You should create a copy of an existing array if you do not wish to modify it.
var hold = ([
]);
var a = [1, 1, 1]
var b = [2];
hold.push({
input: a,
output: b
});
console.log(hold);
a = [...a]; // create a new copy of a
a[2] = 2;
b = [3];
hold.push({
input: a,
output: b
});
console.log(hold);

Just for kicks: Creating/deep-copying multidimensional arrays in JavaScript and accounting for variable length?

I volunteer teaching coding to young girls to code and one of the more advanced ones was trying to use a 2D array for her JavaScript project, but was struggling with the concept of multidimensional arrays. I started putting together a tutorial on arrays and multidimensional arrays to review with her next week, got a little carried away with writing a matrix searching demo, and then realized I don't know a great way of deep copying or creating filled multidimensional arrays that copy the potentially variable-length dimensions of another array (e.g., for storing visited cell data when searching) in JavaScript, which I've only really learned/used within the last year-ish. This is what I came up with:
/**
* #param mdArray A multidimensional array that may contain variable length arrays
* #param fillValue The value to fill the cells with
*
* #return A multidimensional array with the same dimensions as mdArray where
* each cell is filled with fillValue
*/
function createFilledMultidimensionalArray(mdArray, fillValue) {
// Create a new array with mdArray.length rows
return new Array(mdArray.length).fill().map( function (elt, row) {
// Populate each row with a new filled array containing fillValue
return new Array(mdArray[row].length).fill(fillValue);
}
);
}
/**
* #param mdArray A multidimensional array that may contain variable length arrays
*
* #return A deep copy of mdArray
*/
function multidimensionalArrayCopy(mdArray) {
return JSON.parse(JSON.stringify(mdArray));
// note: I'm aware this isn't a universally perfect deep copy *shrug*
}
/* Testing */
// Create a ridiculous array containing variable-length arrays
var multidimensionalArray = [[6, { a: '1', b: 2 }], [1, 2], [3, 4, 5], ['seven']];
// Copy and print the array
var copied = multidimensionalArrayCopy(multidimensionalArray);
console.log(multidimensionalArray);
// Prints: [ [ 6, { a: '1', b: 2 } ], [ 1, 2 ], [ 3, 4, 5 ], [ 'seven' ] ]
// Modify a value
multidimensionalArray[0][1].b = 'hi';
// Print both arrays, observe copy is deep
console.log(multidimensionalArray);
console.log(copied);
/* Prints:
[ [ 6, { a: '1', b: 'hi' } ], [ 1, 2 ], [ 3, 4, 5 ], [ 'seven' ] ]
[ [ 6, { a: '1', b: 2 } ], [ 1, 2 ], [ 3, 4, 5 ], [ 'seven' ] ]
*/
// Create a new array with same dimensions as 'copied' where each cell is filled with 'false'
console.log(createFilledMultidimensionalArray(copied, false));
/* Prints:
[ [ false, false ],
[ false, false ],
[ false, false, false ],
[ false ] ]
*/
Does anyone else out there with more JS experience have any other ideas? (Please don't suggest slice, which shallow copies.)
You could use a recursive clone function instead of stringifying your data. Then for filling multi-dimension arrays, you could use a recursive deepMap function that calls itself on nested arrays:
function clone(value) {
if(Array.isArray(value)) return value.map(clone);
if(typeof value === 'object') {
return Object.entries(value).reduce((cloned, [key, value]) => {
cloned[key] = clone(value);
return cloned;
}, {});
}
return value;
}
function deepMap(array, fn) {
return array.map(value =>
Array.isArray(value)
? deepMap(value, fn)
: fn(value)
);
}
function deepFill(array, fillValue) {
return deepMap(array, () => fillValue);
}
const original = [
[1, 2, 3],
[{value: 'unchanged'}, 5, 6],
[false, true],
[7, 8, [9, 10], 11, 12],
];
const cloned = clone(original);
const filled = deepFill(original, 'fill');
cloned[1][0].value = 'changed';
console.log(
original[1][0].value,
cloned[1][0].value,
);
console.log(filled);

Tournament pairing algorhitm

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
})
}
})

find transposed matrix using lodash [duplicate]

This question already has answers here:
Transposing a 2D-array in JavaScript
(25 answers)
Closed 7 years ago.
I have an array:
[
[1,2,3],
[4,5,6]
]
How can I turn it into
[
[1,4],
[2,5],
[3,6]
]
I am sure it's something I can do easily with lodash but I have not found it in the docs.
a solution using lodash
m = [ [1,2,3], [4,5,6] ];
t = _.zip.apply(_, m);
// result t = [ [1,4], [2,5], [3,6] ]
m = [ [1,1,1,1], [2,2,2,2], [3,3,3,3] ]
t = _.zip.apply(_, m);
// result t = [ [1,2,3], [1,2,3], [1,2,3], [1,2,3] ]
explanation:
apply: Make the call to a function with a given value for this and arguments passed as an array ..... is same that _.zip(arg0, arg1, arg2) similar _.zip.apply(null, m) where m = [arg0, arg1, arg2]
_.zip(['fred', 'barney'], [30, 40], [true, false]);
// → [['fred', 30, true], ['barney', 40, false]]
// Official documentation
Official documentation.
https://lodash.com/docs#zip

Categories