Strongly Connected Component Algorithm - javascript

I have hard time to solve Strongly connected component algorithm.
I have done these implementations blow so far but I got an unexpected result.
DFS and add nodes to stack (named var leader in my code) in order to keep depth of each vertices
Reverse graph edges' direction (getReverseGraph())
Second DFS and create Strongly connected component
I have an issue on step 3 and can not detect component properly.
Expected:
[ [ '1', '7', '5' ], ['2', '4'], [ 6, '3' ] ]
Result:
[ [ '1', '7', '5', '2', '4'], [ 6, '3' ] ].
Would anyone give me some advice? Thanks!
function Graph() {
this.list = {};
}
Graph.prototype.insert = function(newVertex, neighborVertex) {
if (!this.list[newVertex]) {
if (neighborVertex) {
this.list[newVertex] = [neighborVertex];
} else {
this.list[newVertex] = [];
}
} else {
// If neighborVertex is not initialized
if (!this.list[neighborVertex]) {
this.list[neighborVertex] = [];
}
// Add neighborVertex to
this.list[newVertex].push(neighborVertex);
}
};
Graph.prototype.addEdge = function(vertexFrom, vertexTo) {
if (this.list[vertexFrom] || this.list[vertexTo]) {
throw new Error('Vertex does not exsists')
}
this.list[vertexFrom].push(vertexTo);
};
/*
* DFS
*
* #param graph {object}: Takes different graph as optional
* #param vertex {string|integer}
* #param cb {function}
*/
Graph.prototype.dfs = function(graph, vertex, cb) {
// track which node visited
var visited = {};
// Take graph as option
var list = graph ? graph : this.list;
// get initial nodes
var currentNodes = list[vertex];
// Invoke given function for inital node
cb(vertex);
// Mark vertex as visited
visited[vertex] = true;
// If there is no node to traverse return
if (currentNodes.length === 0) {
return;
}
var stack = [...currentNodes];
for (var node of currentNodes) {
visited[node] = true;
}
while (stack.length > 0) {
// Get a node from stack
var nextNode = stack.pop();
// Invoke given function
cb(nextNode);
// Mark the vertex as visited
visited[nextNode] = true;
// Iterate adjacent nodes
if (list[nextNode]) {
// console.log('stack', stack)
for (var neighbor of list[nextNode]) {
// If the vertex is not visited, push each nodes to stack
if (!visited[neighbor]) {
stack.push(neighbor);
visited[neighbor] = true;
}
}
}
}
}
function getReverseGraph(graph) {
var vertices = Object.keys(graph);
var reverseEdgeGraph = {};
for (let vertex of vertices) {
for (let neighbor of graph[vertex]) {
if (reverseEdgeGraph[neighbor]) {
reverseEdgeGraph[neighbor].push(vertex);
} else {
reverseEdgeGraph[neighbor] = [vertex];
}
}
}
return reverseEdgeGraph;
}
Graph.prototype.getStrongComponent = function(vertex) {
if (vertex && !this.list[vertex]) {
throw new("No vertex exsits")
}
vertex = vertex ? vertex.toString() : Object.keys(this.list)[0].toString();
/*
Create Copy of current Adjacency list
*/
var reverseEdgeGraph = getReverseGraph(this.list);
/*
Create Leader
*/
var leader = []; // stack to keep the depth
var keys = Object.keys(this.list);
while (keys.length > 0) {
var indexOfVertex = keys.indexOf(vertex);
keys.splice(indexOfVertex, 1);
this.dfs(null, vertex, function(vertex) {
// If leader does not have the vertex
if (leader.indexOf(vertex) < 0) {
// Get the key (vertex)
var indexOfVertex = keys.indexOf(vertex);
// Delete vertex
keys.splice(indexOfVertex, 1);
// Add vertex to leader
leader.unshift(vertex);
}
});
// Move to next key (remaining node)
vertex = keys[0];
}
/**
*
* Create SCC
*
**/
var allSCC = [];
var visited = {};
while (leader.length > 0) {
var SCC = [];
var target = leader.pop();
if (visited[target]) {
break;
}
this.dfs(reverseEdgeGraph, target, function(vertex) {
// Create component
if (!visited[vertex]) {
visited[vertex] = true;
SCC.push(vertex);
}
});
if (SCC.length > 0) {
allSCC.push(SCC);
}
}
return allSCC
}
function test() {
var graph = new Graph();
var result = [
[2, 4],
[4, 2],
[7, 5],
[5, 1],
[1, 7],
[1, 5],
[5, 7],
[7, 1],
[6, 3],
[3, 6],
[2, 7],
[1, 6]
]
result.forEach(function(line) {
// console.log(line)
graph.insert(line[0], line[1]);
});
var result = graph.getStrongComponent().map(function(components) {
return components.length
}).sort(function(a, b) {
return b - a
});
console.log('result => ', graph.getStrongComponent(1));
}
function dfs() {
var graph = new Graph();
graph.insert('a', 'b');
graph.insert('a', 'g');
graph.insert('b', 'c');
graph.insert('c', 'd');
graph.insert('d', 'e');
graph.insert('e', 'f');
graph.insert('f', 'd');
graph.insert('f', 'g');
graph.insert('g', 'e');
graph.insert('g', 'a');
graph.insert('g', 'b');
graph.insert('g', 'c');
graph.insert('g', 'd');
graph.insert('g', 'h');
graph.dfs(null, 'a', function(v) {
console.log(v);
})
}
// dfs();
test(); // should be [ [ '1', '7', '5' ], ['2', '4'], [ 6, '3' ] ]

You are running the dfs for each node, highly inefficient as you start each dfs with all nodes as unvisited. I added a fourth parameter to your dfs function to keep a single visited object for all the calls. That also avoids repeating nodes in your callback, giving the order you want. Also slightly modified it to be a bit shorter.
With this I refactored both the first dfs traversal and the second one on the reversed graph. Callback is much more cleaner an easier to understand this way, took me a long time to understand what were you doing in your first dfs callback.
Code works now:
function Graph() {
this.list = {};
}
Graph.prototype.insert = function(newVertex, neighborVertex) {
if (!this.list[newVertex]) {
if (neighborVertex) {
this.list[newVertex] = [neighborVertex];
} else {
this.list[newVertex] = [];
}
} else {
// If neighborVertex is not initialized
if (!this.list[neighborVertex]) {
this.list[neighborVertex] = [];
}
// Add neighborVertex to
this.list[newVertex].push(neighborVertex);
}
};
Graph.prototype.addEdge = function(vertexFrom, vertexTo) {
if (this.list[vertexFrom] || this.list[vertexTo]) {
throw new Error('Vertex does not exsists')
}
this.list[vertexFrom].push(vertexTo);
};
/*
* DFS
*
* #param graph {object}: Takes different graph as optional
* #param vertex {string|integer}
* #param cb {function}
*/
Graph.prototype.dfs = function(graph, vertex, cb, visited) {
// track which node visited
var visited = visited || {};
// Take graph as option
var list = graph ? graph : this.list;
if (visited[vertex]) return;
// Mark vertex as visited
visited[vertex] = true;
var stack = [vertex];
while (stack.length > 0) {
// Get a node from stack
var nextNode = stack.pop();
// Invoke given function
cb(nextNode);
// Iterate adjacent nodes
if (list[nextNode]) {
// console.log('stack', stack)
for (var neighbor of list[nextNode]) {
// If the vertex is not visited, push each nodes to stack
if (!visited[neighbor]) {
stack.push(neighbor);
visited[neighbor] = true;
}
}
}
}
}
function getReverseGraph(graph) {
var vertices = Object.keys(graph);
var reverseEdgeGraph = {};
for (let vertex of vertices) {
for (let neighbor of graph[vertex]) {
if (reverseEdgeGraph[neighbor]) {
reverseEdgeGraph[neighbor].push(vertex);
} else {
reverseEdgeGraph[neighbor] = [vertex];
}
}
}
return reverseEdgeGraph;
}
Graph.prototype.getStrongComponent = function(vertex) {
if (vertex && !this.list[vertex]) {
throw new("No vertex exsits")
}
vertex = vertex ? vertex.toString() : Object.keys(this.list)[0].toString();
/*
Create Copy of current Adjacency list
*/
var reverseEdgeGraph = getReverseGraph(this.list);
var stack = [];
var visited = {}
for (var vertex in this.list) {
this.dfs(null, vertex, function(v) {
stack.push(v);
}, visited)
}
/**
*
* Create SCC
*
**/
var allSCC = [];
visited = {};
stack.reverse().forEach((vertex) => {
var SCC = []
this.dfs(reverseEdgeGraph, vertex, function(v) {
SCC.push(v);
}, visited)
if (SCC.length) allSCC.push(SCC)
})
return allSCC
}
function test() {
var graph = new Graph();
var result = [
[2, 4],
[4, 2],
[7, 5],
[5, 1],
[1, 7],
[1, 5],
[5, 7],
[7, 1],
[6, 3],
[3, 6],
[2, 7],
[1, 6]
]
result.forEach(function(line) {
// console.log(line)
graph.insert(line[0], line[1]);
});
console.log('result => ', graph.getStrongComponent(1));
}
// dfs();
test(); // should be [ [ '1', '7', '5' ], ['2', '4'], [ 6, '3' ] ]

Related

Convert an Array of 3 numbers to associated letters

Im learning JS and have set myself the challenge to re-create a football league table, from an array of match results.
Everything is going really well and I'm almost complete, but I can't filter my 'FORM GUIDE' array to their corresponding Letters.
I have the points scored over the last 5 games ONLY outputted as an array (below);
[3, 1, 3, 0, 3]
But I'd like to output this as 3 = W, 1 = D, 0 = L.
So...
W, D, W, L, W
Can someone please explain how I can do this?
Thank you
'use strict';
// Tottenham's Premier League scores & points 21-22 //
//--------------------------------------------------//
const scoresPoints = [
[1, 0, 3], // Watford
[1, 1, 1], // Southampton
[3, 0, 3], // Crystal Palace
[2, 2, 1], // Liverpool
[3, 0, 3], // Norwich
[2, 0, 3], // Brentford
[2, 1, 3], // Leeds
[0, 0, 1], // Everton
[3, 2, 3], // Newcastle
[0, 3, 0], // Man U
[0, 1, 0], // West Ham
[2, 1, 3], // Villa
[1, 3, 0], // Arsenal
[0, 3, 0], // Chelsea
[0, 3, 0], // Crystal Palace
[1, 0, 3], // Watford
[1, 0, 3], // Wolves
[1, 0, 3], // Man City
];
// Define The functions & arrays--------------------//
//--------------------------------------------------//
let tottenhamScores;
let totalTottenhamGoals;
let totalTottenhamPoints;
let totalOpponentsGoals;
let tottenhamForm = [];
// The goals scored by Tottenham
const tottenhamGoals = [];
// The points scored by Tottenham
const tottenhamPoints = [];
// The goals scored by the opponents
const opponentsGoals = [];
// Filter the data from each array------------------//
//--------------------------------------------------//
for (let i = 0; i < scoresPoints.length; i++) {
tottenhamScores = scoresPoints[i][0];
// Just Tottenham's goals
tottenhamGoals.push(tottenhamScores);
// Just Tottenham's points
const leaguePoints = scoresPoints[i][2];
tottenhamPoints.push(leaguePoints);
// Just the opponents goals
const opponentsScores = scoresPoints[i][1];
opponentsGoals.push(opponentsScores);
// Just Tottenham's Form
const leagueForm = scoresPoints[i][2];
tottenhamForm.push(leagueForm);
}
// Adding up the arrays-----------------------------//
//--------------------------------------------------//
// Adding up Tottenham's goals
for (let i = 0; i < tottenhamGoals.length; i++) {
totalTottenhamGoals = tottenhamGoals.reduce(function (a, b) {
return a + b;
}, 0);
}
// Adding up Tottenham's points
for (let i = 0; i < tottenhamPoints.length; i++) {
totalTottenhamPoints = tottenhamPoints.reduce(function (a, b) {
return a + b;
}, 0);
}
// Adding up the opponents goals
for (let i = 0; i < opponentsGoals.length; i++) {
totalOpponentsGoals = opponentsGoals.reduce(function (a, b) {
return a + b;
}, 0);
}
// Last 5 games-------------------------------------//
//--------------------------------------------------//
// Find the individual values
function occurrence(pointValues, value) {
return pointValues.filter(v => v === value).length;
}
const win = occurrence(tottenhamPoints, 3);
const draw = occurrence(tottenhamPoints, 1);
const loss = occurrence(tottenhamPoints, 0);
// Filter to last five games
function lastFiveGames(form, five) {
form = tottenhamForm.slice(0, five);
return form;
}
const latestForm = lastFiveGames(tottenhamForm, 5);
// Convert points to represented letters
const letteredResult = latestForm.map(result => {
switch (result) {
case 0:
return 'L';
case 1:
return 'D';
case 3:
return 'W';
default:
return 'No Form To Display';
}
});
// Print the statement & table----------------------//
//--------------------------------------------------//
console.log(
`SUMMARY
--------
--------
Throughout the 2021-22 Premier League season, Tottenham Hotspur have scorred ${totalTottenhamGoals} goals and conceeded ${totalOpponentsGoals}.
This has gained Tottenham Hotspur ${totalTottenhamPoints} points to date.
(Dropping ${
scoresPoints.length * 3 - totalTottenhamPoints
} points throughout the season from the maximum of ${
scoresPoints.length * 3
} available).
TABLE & FORM GUIDE ---
----------------------
${scoresPoints.length} Played
${win} Wins
${draw} Draws
${loss} Losses
${totalTottenhamGoals} For
${totalOpponentsGoals} Against
${totalTottenhamGoals - totalOpponentsGoals} Goal Difference
${totalTottenhamPoints} POINTS IN TOTAL
FORM (Last 5 Games) ${letteredResult}
----------------------
----------------------`
);
You can achieve this with Array.map and a nice switch statement. Try this way:
const arr = [3, 1, 3, 0, 3]
const matched = arr.map(result => {
switch(result){
case 0:
return 'L'
case 1:
return 'D'
case 3:
return 'W'
default:
return 'X'
}
})
console.log(matched)
You can use map:
[3, 1, 3, 0, 3].map(e => { if (e == 3) return "W"; if (e == 1) return "D"; if (e == 0) return "L"; throw `Unexpected value: ${e}`; })
let arr = [3, 1, 3, 0, 3];
let arrMap = arr.map((i) => {
if (i == 3) return "W";
if (i == 0) return "L";
return "D"
})
console.log(arrMap)
You can achieve this clean and simple using map and an object acting as Enum where the key will represent the points and the value will represent the result char.
const array = [3, 1, 3, 0, 3];
const cases = {0: 'L', 1: 'D', 3: 'W'};
const matches = array.map(e => cases[e]);
console.log(matches)
More about map - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Instead of using map, you could also use forEach. The main difference is that map will return a new array, whereas forEach doesn't return anything and can be used to change the original array. Here is how:
const logic = {
3: 'W',
1: 'D',
0: 'L'
}
const data = [3, 1, 3, 0, 3];
data.forEach((value, key) => data[key] = logic[value]);
console.log(data);
const arr = [3, 1, 3, 0, 3, 2, 4];
const data = arr.map(v => ['L','D','_','W'][v] || '_');
console.log( data );

maximize the groups of combinations to use all values

Given an array of combinations (to a certain sum), I'm struggling to find pairs of unique combinations that use all the numbers from the source.
function maximizeGroups(groups, source) {}
const groups = [[10], [1, 3, 6], [1, 9], [3, 7], [4, 6]];
const source = [6, 1, 3, 10, 4, 7, 9]
console.log(maximizeGroups(groups, source))
-> expected result [[6, 4], [7, 3], [10], [9, 1]]
Here [1, 3, 6] is discarded.
Since use of this combination doesn't allow to use the number 4
Function used:
const groups = [[10], [1, 3, 6], [1, 9], [3, 7], [4, 6]];
const source = [6, 1, 3, 10, 4, 7, 9];
function maximizeGroups(groups, source) {
const sorted = source
.slice()
.sort((a, b) => a - b)
.join('');
for (let i = 0; i < groups.length - 1; i++) {
const copy = groups.slice();
copy.splice(i, 1);
const check = copy
.slice()
.flat()
.sort((a, b) => a - b)
.join('');
if (check === sorted) {
return copy;
}
}
return null;
}
Here is what should be a more efficient solution. Basically as I'm piecing together the groupings I'm making sure that I'm only considering groups that could wind up summing to the target.
If you have a large number of groups, and there is no solution, this could fail in exponential time. But normally it will finish fairly quickly.
// Helper functions to extract the parts of a "pos:value" pair.
function pairToPos (pair) {
return Number(pair.split(":")[0]);
}
function pairToValue (pair) {
return Number(pair.split(":")[1]);
}
function sum (nums) {
let total = 0;
nums.forEach( (value) => {total += value} );
return total;
}
function recursiveSolution(nums, target, maxGroups, sumLookup, pos, partialAnswers) {
if (pos == nums.length) {
return partialAnswers;
}
// Try starting a new group.
if ((partialAnswers.length < maxGroups)
&& ([pos, target].join(":") in sumLookup)) {
// Try adding a new group.
partialAnswers.push([nums[pos]]);
let attempt = recursiveSolution(nums, target, maxGroups, sumLookup, pos+1, partialAnswers);
if (attempt != null) {
return partialAnswers;
}
// Get rid of my new group.
partialAnswers.pop();
}
// Try adding this value to each group.
let finalAnswers = null;
partialAnswers.forEach( (group) => {
if (finalAnswers != null) {
// Do nothing, we have the answer.
}
else if ([pos, target - sum(group)].join(":") in sumLookup) {
// Try adding this value.
group.push(nums[pos]);
let attempt = recursiveSolution(nums, target, maxGroups, sumLookup, pos+1, partialAnswers);
if (attempt != null) {
finalAnswers = partialAnswers;
}
else {
// Get rid of my new value.
group.pop();
}
}
});
return finalAnswers;
}
function deriveGroups(nums, target) {
// Use dynamic programming to find all paths to the target.
let sumLookup = {};
// We constantly use "pos:value" pairs as keys.
// This entry means "the empty sum off the array is 0".
sumLookup[[nums.length, 0].join(":")] = null;
// We go backwards here to get the future sum from current value + pos.
for (let i = nums.length-1; 0 <= i; i--) {
let term = nums[i];
Object.keys(sumLookup).forEach( (pair) => {
let prevPos = pairToPos(pair);
let prevValue = pairToValue(pair);
let nextPair = [i, prevValue + term].join(":");
if (! (nextPair in sumLookup)) {
sumLookup[nextPair] = [];
}
sumLookup[nextPair].push(prevPos);
});
}
return recursiveSolution(nums, target, sum(nums)/target, sumLookup, 0, []);
}
console.log(deriveGroups([9, 2, 13, 10, 2, 3], 13));

Have one array reference two or more

So I want to know if it's possible to create a scenario like this in javascript:
Lets say I have two arrays:
a = [1, 2, 3]
b = [4, 5, 6]
And then I would like to create an array c that references the two arrays:
c = [1, 2, 3, 4, 5, 6]
However when I change an element in one of the two arrays I would also like to have it automatically affect array c:
a[1] = 1
c = [1, 1, 3, 4, 5, 6]
Is there a way to make this possible in javascript?
The traditional pass-by-reference approach doesn't exist in JavaScript, so you'll have to use a workaround. The simplest way would be to return c dynamically as a combination of a and b using a function. Another would be using an object and getter/setters to manipulate what you get when accessing c.
Example:
var $ = (function () {
/* Hidden arrays */
var a = [1, 2, 3];
var b = [4, 5, 6];
/* Visible object. */
return {
get a () {
return a;
},
set a (v) {
a = v;
},
get b () {
return b;
},
set b (v) {
b = v;
},
get c () {
return a.concat(b);
},
};
})();
/* Example */
console.log(JSON.stringify($.a), JSON.stringify($.b), JSON.stringify($.c));
$.a = [0, 1, 2];
console.log(JSON.stringify($.a), JSON.stringify($.b), JSON.stringify($.c));
$.b[1] = 7;
console.log(JSON.stringify($.a), JSON.stringify($.b), JSON.stringify($.c));
You will need a Proxy.
const a = [1,2,3];
const b = [4,5,6];
const c = new Proxy([a,b], {
_localIndex: function(arrays, index) {
index = +index;
if( isNaN(index)) throw new TypeError("Expected numeric index");
if( Math.floor(index) !== index) throw new RangeError("Index must be an integer");
if( index < 0) throw new RangeError("Index must be positive");
for( let i=0; i<arrays.length; i++) {
if( arrays[i].length > index) return [i,index];
index -= arrays[i].length;
}
throw new RangeError("Index out of bounds");
},
get: function(arrays, index) {
if( index === "length") {
return arrays.reduce((a,c)=>a+c.length,0);
}
if( index === "source") {
return arrays;
}
const [arr, idx] = this._localIndex(arrays, index);
return arrays[arr][idx];
},
set: function(arrays, index, value) {
const [arr, idx] = this._localIndex(arrays, index);
arrays[arr][idx] = value;
}
});
console.log("Get c[4]: "+c[4]);
c[2] = 9;
console.log("Updated c[2], a is now: "+JSON.stringify(a));
console.log("Get c's source array: "+JSON.stringify(c.source));
a.push('x');
console.log("Pushed value to a, c is now: "+JSON.stringify(c.source));
MDN docs

Find Max value of each x value in JavaScript multidimensional array

I want to 'reduce' the array to only max values for each x (or index 0) value in a JavaScript multidimensional array.
My Array is as follows:
var mulitple = [["1/2013", 1],
["1/2013", 5],
["1/2013", 7],
["1/2013", 6],
["1/2013", 5],
["2/2013", 7],
["2/2013", 10],
["2/2013", 10],
["3/2013", 7],
["3/2013", 10],
["3/2013", 10],
["4/2013", 1],
["4/2013", 5],
["4/2013", 7],
["4/2013", 6],
["4/2013", 5],
["5/2013", 7]];
So the final result should be as follows:
[["1/2013", 7],
["2/2013", 10],
["3/2013", 10],
["4/2013", 7],
["5/2013", 7]];
How can I achieve this in JavaScript.
EDIT:
Aww man who voted my question down.
Anyhow, this is what I have come up with.
var max = 0;
var newarray = [];
for (var i = 1; i < mulitple.length; i++) {
if (mulitple[i - 1][0] == mulitple[i][0]) {
if (mulitple[i - 1][1] > max) {
max = mulitple[i - 1][1];
}
}
else {
if (mulitple[i - 1][1] > max) {
max = mulitple[i - 1][1];
}
newarray.push([mulitple[i - 1][0], max]);
max = 0;
}
}
newarray.push([mulitple[mulitple.length - 1][0], max]);
The problem that I am having is that I can't get that last value (for the lone record) to get in the array. This was my result after I ran the code above.
[["1/2013", 7], ["2/2013", 10], ["3/2013", 10], ["4/2013", 7], ["5/2013", 0]]
This assumes that original array is already sorted. If not, you will have to write additional function to sort out.
function findMaximums(data) {
var out = [], maximums = {}, order = new Set;
data.reduce(function(acc, pair) {
if (
// Accumulator has value and it's lower than current
(acc[pair[0]] && acc[pair[0]][1] < pair[1]) ||
// Accumulator doesn't have value
!acc[pair[0]]
) {
acc[pair[0]] = pair; // Store maximum in accumulator
order.add(pair[0]) // Store order in set
}
return acc;
}, maximums);
order.forEach(function(key) {
out.push(maximums[key]); // Populate out with maximums by order
});
return out;
}
findMaximums(multiple);
/*[
[
"1/2013",
7
],
[
"2/2013",
10
],
[
"3/2013",
10
],
[
"4/2013",
7
],
[
"5/2013",
7
]
]*/
Update 1: same, but without Set.
function findMaximums(data) {
var order = [];
var maximums = data.reduce(function(acc, pair) {
if (
// Accumulator has value and it's lower than current
(acc[pair[0]] && acc[pair[0]][2] < pair[1]) ||
// Accumulator doesn't have value
!acc[pair[0]]
) {
// Store maximum
acc[pair[0]] = pair;
// Store order
if (order.indexOf(pair[0]) === -1) {
order.push(pair[0])
}
}
return acc;
}, {});
return order.map(function(key) {
return maximums[key]; // Populate out with maximums by order
});
}
Update 2: Shorter version.
function findMaximums(data) {
return data.filter(function(p1, i1) {
return !data.some(function(p2, i2) {
return p1[0] === p2[0] && ( (p1[1] < p2[1]) || (p1[1] === p2[1] && i1 > i2) );
});
});
}
In this version I let pair to remain in output if there are no other pairs in input data that:
Have same month.
Have bigger value.
or
Have same value, but occur earlier than tested pair. This prevents duplicates.
Read here more about used Array methods: filter, some.
Assuming the array as defined in the original question, which is sorted to have each grouping together...
Completely untested code:
var reduced = [];
var groupName = '';
var groupMax;
var groupIndex;
var l = multiple.length; // Grab the array length only once
for (var i = 0; i < l; i++){
// Current Group Name doesn't match last Group Name
if (multiple[i][0] !== groupName) {
// Last Group Name is not empty (it's not the first Group)
if (groupName !== '') {
// Assume groupIndex has been set and grab the item
reduced.push(multiple[groupIndex]);
}
// Grab the new Group Name and set the initial Max and Index
groupName = multiple[i][0];
groupMax = multiple[i][1];
groupIndex = i;
}
// Current Value is bigger than last captured Group Max
if (multiple[i][1] > groupMax) {
// Grab the new Group Max and the current Index
groupMax = multiple[i][1];
groupIndex = i;
}
}
// Grab the last index, since there's no new group after the last item
reduced.push(multiple[groupIndex]);
There could be some syntax or logic errors. I didn't actually run this code, but I think the concept is correct.
Here's a tested version using a map to collect all the unique values, then the output is sorted by month/year and is independent of the order of the input data. This also works in all browsers (IE6+).
Working demo: http://jsfiddle.net/jfriend00/dk1tc73s/
function findLargest(list) {
var map = {}, output = [], item, key, val, current;
for (var i = 0; i < list.length; i++) {
item = list[i];
key = item[0];
val = item[1];
current = map[key];
if (current) {
// this date is in the map, so make sure to save the largest
// value for this date
if (val > current) {
map[key] = val;
}
} else {
// this date is not yet in the map, so add it
map[key] = val;
}
}
// map contains all the largest values, output it to an array
// the map is not in a guaranteed order
for (var key in map) {
output.push([key, map[key]])
}
// actually parse to numbers in sort so the comparison
// works properly on number strings of different lengths
function parseDate(str) {
var split = str.split("/");
return {m: +split[0], y: +split[1]};
}
// now sort the output
output.sort(function(t1, t2) {
var diffYear, a, b;
a = parseDate(t1[0]);
b = parseDate(t2[0]);
diffYear = a.y - b.y;
if (diffYear !== 0) {
return diffYear;
} else {
return a.m - b.m;
}
});
return output;
}

Convert simple array into two-dimensional array (matrix)

Imagine I have an array:
A = Array(1, 2, 3, 4, 5, 6, 7, 8, 9);
And I want it to convert into 2-dimensional array (matrix of N x M), for instance like this:
A = Array(Array(1, 2, 3), Array(4, 5, 6), Array(7, 8, 9));
Note, that rows and columns of the matrix is changeable.
Something like this?
function listToMatrix(list, elementsPerSubArray) {
var matrix = [], i, k;
for (i = 0, k = -1; i < list.length; i++) {
if (i % elementsPerSubArray === 0) {
k++;
matrix[k] = [];
}
matrix[k].push(list[i]);
}
return matrix;
}
Usage:
var matrix = listToMatrix([1, 2, 3, 4, 4, 5, 6, 7, 8, 9], 3);
// result: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
You can use the Array.prototype.reduce function to do this in one line.
ECMAScript 6 style:
myArr.reduce((rows, key, index) => (index % 3 == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows, []);
"Normal" JavaScript:
myArr.reduce(function (rows, key, index) {
return (index % 3 == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows;
}, []);
You can change the 3 to whatever you want the number of columns to be, or better yet, put it in a reusable function:
ECMAScript 6 style:
const toMatrix = (arr, width) =>
arr.reduce((rows, key, index) => (index % width == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows, []);
"Normal" JavaScript:
function toMatrix(arr, width) {
return arr.reduce(function (rows, key, index) {
return (index % width == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows;
}, []);
}
This code is generic no need to worry about size and array, works universally
function TwoDimensional(arr, size)
{
var res = [];
for(var i=0;i < arr.length;i = i+size)
res.push(arr.slice(i,i+size));
return res;
}
Defining empty array.
Iterate according to the size so we will get specified chunk.That's why I am incrementing i with size, because size can be 2,3,4,5,6......
Here, first I am slicing from i to (i+size) and then I am pushing it to empty array res.
Return the two-dimensional array.
The cleanest way I could come up with when stumbling across this myself was the following:
const arrayToMatrix = (array, columns) => Array(Math.ceil(array.length / columns)).fill('').reduce((acc, cur, index) => {
return [...acc, [...array].splice(index * columns, columns)]
}, [])
where usage would be something like
const things = [
'item 1', 'item 2',
'item 1', 'item 2',
'item 1', 'item 2'
]
const result = arrayToMatrix(things, 2)
where result ends up being
[
['item 1', 'item 2'],
['item 1', 'item 2'],
['item 1', 'item 2']
]
How about something like:
var matrixify = function(arr, rows, cols) {
var matrix = [];
if (rows * cols === arr.length) {
for(var i = 0; i < arr.length; i+= cols) {
matrix.push(arr.slice(i, cols + i));
}
}
return matrix;
};
var a = [0, 1, 2, 3, 4, 5, 6, 7];
matrixify(a, 2, 4);
http://jsfiddle.net/andrewwhitaker/ERAUs/
Simply use two for loops:
var rowNum = 3;
var colNum = 3;
var k = 0;
var dest = new Array(rowNum);
for (i=0; i<rowNum; ++i) {
var tmp = new Array(colNum);
for (j=0; j<colNum; ++j) {
tmp[j] = src[k];
k++;
}
dest[i] = tmp;
}
function matrixify( source, count )
{
var matrixified = [];
var tmp;
// iterate through the source array
for( var i = 0; i < source.length; i++ )
{
// use modulous to make sure you have the correct length.
if( i % count == 0 )
{
// if tmp exists, push it to the return array
if( tmp && tmp.length ) matrixified.push(tmp);
// reset the temporary array
tmp = [];
}
// add the current source value to the temp array.
tmp.push(source[i])
}
// return the result
return matrixified;
}
If you want to actually replace an array's internal values, I believe you can call the following:
source.splice(0, source.length, matrixify(source,3));
This a simple way to convert an array to a two-dimensional array.
function twoDarray(arr, totalPerArray) {
let i = 0;
let twoDimension = []; // Store the generated two D array
let tempArr = [...arr]; // Avoid modifying original array
while (i < arr.length) {
let subArray = []; // Store 2D subArray
for (var j = 0; j < totalPerArray; j++) {
if (tempArr.length) subArray.push(tempArr.shift());
}
twoDimension[twoDimension.length] = subArray;
i += totalPerArray;
}
return twoDimension;
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
twoDarray(arr, 3); // [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]
function changeDimension(arr, size) {
var arrLen = arr.length;
var newArr = [];
var count=0;
var tempArr = [];
for(var i=0; i<arrLen; i++) {
count++;
tempArr.push(arr[i]);
if (count == size || i == arrLen-1) {
newArr.push(tempArr);
tempArr = [];
count = 0;
}
}
return newArr;
}
changeDimension([0, 1, 2, 3, 4, 5], 4);
function matrixify(array, n, m) {
var result = [];
for (var i = 0; i < n; i++) {
result[i] = array.splice(0, m);
}
return result;
}
a = matrixify(a, 3, 3);
function chunkArrToMultiDimArr(arr, size) {
var newArray = [];
while(arr.length > 0)
{
newArray.push(arr.slice(0, size));
arr = arr.slice(size);
}
return newArray;
}
//example - call function
chunkArrToMultiDimArr(["a", "b", "c", "d"], 2);
you can use push and slice like this
var array = [1,2,3,4,5,6,7,8,9] ;
var newarray = [[],[]] ;
newarray[0].push(array) ;
console.log(newarray[0]) ;
output will be
[[1, 2, 3, 4, 5, 6, 7, 8, 9]]
if you want divide array into 3 array
var array = [1,2,3,4,5,6,7,8,9] ;
var newarray = [[],[]] ;
newarray[0].push(array.slice(0,2)) ;
newarray[1].push(array.slice(3,5)) ;
newarray[2].push(array.slice(6,8)) ;
instead of three lines you can use splice
while(array.length) newarray.push(array.splice(0,3));
const x: any[] = ['abc', 'def', '532', '4ad', 'qwe', 'hf', 'fjgfj'];
// number of columns
const COL = 3;
const matrix = array.reduce((matrix, item, index) => {
if (index % COL === 0) {
matrix.push([]);
}
matrix[matrix.length - 1].push(item);
return matrix;
}, [])
console.log(matrix);
Using the Array grouping proposal (currently stage 3), you can now also do something like the following:
function chunkArray(array, perChunk) {
return Object.values(array.group((_, i) => i / perChunk | 0));
}
See also the MDN documentation for Array.prototype.group().
Simplest way with ES6 using Array.from()
const matrixify = (arr, size) =>
Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
arr.slice(i * size, i * size + size));
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] ;
console.log(matrixify(list, 3));
Another stab at it,
Creating an empty matrix (Array of row arrays)
Iterating arr and assigning to matching rows
function arrayToMatrix(arr, wantedRows) {
// create a empty matrix (wantedRows Array of Arrays]
// with arr in scope
return new Array(wantedRows).fill(arr)
// replace with the next row from arr
.map(() => arr.splice(0, wantedRows))
}
// Initialize arr
arr = new Array(16).fill(0).map((val, i) => i)
// call!!
console.log(arrayToMatrix(arr, 4));
// Trying to make it nice
const arrToMat = (arr, wantedRows) => new Array(wantedRows).fill(arr)
.map(() => arr.splice(0, wantedRows))
(like in: this one)
(and: this one from other thread)
MatArray Class?
Extending an Array to add to a prototype, seems useful, it does need some features to complement the Array methods, maybe there is a case for a kind of MatArray Class? also for multidimensional mats and flattening them, maybe, maybe not..
1D Array convert 2D array via rows number:
function twoDimensional(array, row) {
let newArray = [];
let arraySize = Math.floor(array.length / row);
let extraArraySize = array.length % row;
while (array.length) {
if (!!extraArraySize) {
newArray.push(array.splice(0, arraySize + 1));
extraArraySize--;
} else {
newArray.push(array.splice(0, arraySize));
}
}
return newArray;
}
function twoDimensional(array, row) {
let newArray = [];
let arraySize = Math.floor(array.length / row);
let extraArraySize = array.length % row;
while (array.length) {
if (!!extraArraySize) {
newArray.push(array.splice(0, arraySize + 1));
extraArraySize--;
} else {
newArray.push(array.splice(0, arraySize));
}
}
return newArray;
}
console.log(twoDimensional([1,2,3,4,5,6,7,8,9,10,11,12,13,14], 3))
Short answer use:
const gridArray=(a,b)=>{const d=[];return a.forEach((e,f)=>{const
h=Math.floor(f/b);d[h]=d[h]||[],d[h][f%b]=a[f]}),d};
Where:
a: is the array
b: is the number of columns
An awesome repository here .
api : masfufa.js
sample : masfufa.html
According to that sample , the following snippet resolve the issue :
jsdk.getAPI('my');
var A=[1, 2, 3, 4, 5, 6, 7, 8, 9];
var MX=myAPI.getInstance('masfufa',{data:A,dim:'3x3'});
then :
MX.get[0][0] // -> 1 (first)
MX.get[2][2] // ->9 (last)

Categories

Resources