The lodash way to delete nested array elements - javascript

I have an array like:
[[0,1,2,3][0,1,2,3,][0,1,2,3][0,1,2,3]]
I want to slice the nested arrays to keep only the first two elements.
I am using this code with lodash:
for (i = 0; i < data.length; ++i) {
data[i] = _.slice(data[i], [start=0], [end=2]);
}
This doesn't feel very lodash though. How would you approach it?

You can achieve it this way:
var data = [[0,1,2,3],[0,1,2,3,],[0,1,2,3],[0,1,2,3]];
_.invoke(data, 'slice', 0, 2);
Otherwise, if you want to use map:
data.map(function(item) { return item.slice(0, 2); }); // pure js solution
_.map(data, function(item) { return _.slice(item, 0, 2); } );

Here's what I would do:
var collection = [
[ 0, 1, 2, 3 ],
[ 0, 1, 2, 3 ],
[ 0, 1, 2, 3 ],
[ 0, 1, 2, 3 ]
];
_.map(collection, _.ary(_.partialRight(_.take, 2), 1));
// → [ [ 0, 1 ], [ 0, 1 ], [ 0, 1 ], [ 0, 1 ] ]
Here's what's going on:
The ary() function is making sure the callback only gets one argument passed to it, the collection item.
The partialRight() function is partially-applying 2 to the take() function as the second argument. The first argument will be the collection item.
The take() function is taking the first n items from the array.

Related

Javascript: Push function behave differently with array with reference and without reference [duplicate]

This question already has answers here:
Is JavaScript a pass-by-reference or pass-by-value language?
(33 answers)
Closed 1 year ago.
I am getting two different results for doing which I believe is the same thing in javascript with array push. Since I am new to javascript, I might be missing the big picture here.
Sample 1 -
class Test {
constructor(){
this.matrix = [];
}
createMatrix(){
this.matrix.push([0,0,0]);
this.matrix.push([0,0,0]);
this.matrix.push([0,0,0]);
}
addNodes(x, y){
this.matrix[x][y] = 1;
this.matrix[y][x] = 1;
}
printMatrix(){
return this.matrix;
}
}
let test = new Test();
test.createMatrix();
console.log(test.printMatrix());
test.addNodes('1','2');
console.log(test.printMatrix());
O/p -
[ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ]
[ [ 0, 0, 0 ], [ 0, 0, 1 ], [ 0, 1, 0 ] ]
Sample 2 -
Replacing the createMatrix method with below one, gives me another result when trying to addNodes.
createMatrix(){
let row = [0,0,0];
this.matrix.push(row);
this.matrix.push(row);
this.matrix.push(row);
}
O/p -
[ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ]
[ [ 0, 1, 1 ], [ 0, 1, 1 ], [ 0, 1, 1 ] ]
Please help me understand what I am missing out here, I want to achieve is o/p of sample 1 with the sample 2 code.
it is a matter of reference. In the second example, you are inputting the same array 3 times with the push function. The 3 arrays are pointing to the same memory address, which is referenced by the variable rows.
In the first example there are 3 different arrays, with 3 different memory addresses

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

How do I segment this array according to returned key in Vue JavaScript?

I have the following array which contains 132 action_id's:
[ { "id": 1, "project_id": 6, "action_1": 0, "action_2": 0, "action_3": 0, "action_4": 0, "action_5": 0, "action_6": 0, "action_7": 0, "action_8": 0 ..."action_132": 0]
This is returned here:
progress() {
return this.$store.getters['progress/getProgress']
}
The action_ id's are numbered for a particular reason -they all belong to Tasks, of which there are 11.
For instance:
action_1 - action_3 belong to Task 1,
action_4 - action_8 belong to Task 2,
action_9 - action_11 belong to Task 3, and so on...
How can I write a JavaScript function that would segment the actions into Task arrays?
So I would have something like
Task1:
[
action_1,
action_2,
action_3,
]`
`Task2:
[
action_4,
action_5,
action_6,
action_7,
action_8
]`
`Task3:
[
action_9,
action_10,
action_11,
]`...
`Task11:
[
action_128,
action_129,
action_130,
action_131,
action_132
]
I already know which action_id's belong to which Task so in my mind I imagine something like:
Loop through `progress` {
if action_1 to action_3 {
Task1.push
}
if action_4 to action_8 {
Task2.push
}
if action_9 to action_11 {
Task3.push
}
}
What would be the correct syntax to make this happen?
I assume the process is the task object { "id": 1, "project_id": 6, "action_1": 0, "action_2": 0, "action_3": 0, ..."action_132": 0
Object.keys(process).map(key => {
if (key.includes('action')) {
const actionNumber = key.substring(7) // get action number
if (1 <= actionNumber <= 3) {
task1.push(process[key]) // push the value of that action to task
}
... for another eondition
}
})

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

Categories