What is the best approach to solve the following problem in JavaScript?
Read also about Double Arraylist Java
There is a multidimensional array ‘Teams’ with multiple arrays that have a TeamName (string), WinRecord (number), LossRecord (number), and a Sort_Key (number) in this format: [TeamName, W, L, sort_key]
The data looks like this:
Teams[
['Team A', 25, 2, 88],
['Team B', 20, 7, 84],
['Team C', 20, 7, 76],
['Team D', 20, 7, 54],
['Team E', 20, 7, 65],
['Team F', 20, 6, 34],
['Team G', 19, 8, 67],
['Team H', 20, 7, 11],
...
['Team N', 3-24, 33]
]
The previous data is in an arbitrary order for a reason.
The task here is to look inside the Multidimensional Array “Teams” where the W-L record is the same in the adjacent arrays. From the previous data, teams B, C, D and E have the same W-L record and they are all adjacent to each other. Team F does NOT have the same because although it matches the W but it does not match the L. Notice that team H has the same 20-7 record but since it is NOT adjacent, we do not consider it for our comparison!
Now need to re-organise the adjacent teams B, C, D and E , in ASC order by sort_key. The arrays that do not have a matching W-L with another array must stay in the same position. Therefore the solution would look like this:
Teams[
['Team A', 25, 2, 88], //stays in the same place because it has no W-L match adjacent
['Team D', 20, 7, 54], //sort_key is the lowest of this "chunk" so this goes first
['Team E', 20, 7, 65], //sort_key second in ASC order
['Team C', 20, 7, 76], //then this one
['Team B', 20, 7, 84], //finally this is the highest sort_key of this "chunk"
['Team F', 20, 6, 34], //stays in the same place because it has no W-L match adjacent
['Team G', 19, 8, 67], //stays in the same place because it has no W-L match adjacent
['Team H', 20, 7, 11], //stays in the same place because its NOT ADJACENT to the last 20-7 team
...
['Team N', 3-24, 33] //stays in the same place because it has no W-L match adjacent
]
Note: The previous example has adjacent arrays with the same W-L of 20-7 , but there might be more “chunks” with matching W-L arrays that are adjacent to each other, this may happen multiple times until we reach array at position ‘n’.
Some solutions that could work are ass follows:
- To take the first array and compare with the next array, if there is a W-L match to check the next one, n times until there is no further match. If there is no match we move on to the next array and perform this step again.
- If there is an adjacent W-L we need to keep checking the next array until there is no exact match and we stop. All the adjacent matching arrays can be copied to a temporary array. We need to save “from” the original_position because if we take data from Teams[2] all the way to Teams[5] we need to re-insert the sorted values later starting at position 2. Maybe save this original_position into a variable?
- We now sort the temporary array by sort_key ASC. We now copy the temp array into the original Teams array “from” the original_position.
- We keep going looking for more matches like this until we reach position ‘n’ and we can exit and return the finalized properly sorted Teams array.
SOLUTION
Write a generic groupAdjacent
function. It has two parameters:
t
– the input arrayequal
– a function to test if elements are considered adjacent
function groupAdjacent(t, equal)
{ function* run (r, q, i)
{ if (i >= t.length)
return yield [...r, q]
if (equal(q, t[i]))
return yield* run([...r, q], t[i], i + 1)
yield [...r, q]
yield* run([], t[i], i + 1)
}
return t.length
? Array.from(run([], t[0], 1))
: []
}
After that define two functions specific to the particular teams
data:
teamAdjacent
– determines what makes a team adjacencyteamSort
– compares two teams for sorting
const teamAdjacent = (a, b) =>
a[1] == b[1] && a[2] == b[2] // w/l is equal
const teamSort = (a, b) =>
a[3] - b[3] // sort by sortKey, ascending
Finally tie everything together.
groupAdjacent
creates an array of groups, then .sort
each group, and lastly call .flat
to return an un-grouped result
:
const result =
groupAdjacent(teams, teamAdjacent)
.map(_ => _.sort(teamSort))
.flat()
console.log(result)
['Team A', 25, 2, 88]
['Team D', 20, 7, 54]
['Team E', 20, 7, 65]
['Team C', 20, 7, 76]
['Team B', 20, 7, 84]
['Team F', 20, 6, 34]
['Team G', 19, 8, 67]
['Team H', 20, 7, 11]
['Team N', 3, 24, 33]
function groupAdjacent(t, equal)
{ function* run (r, q, i)
{ if (i >= t.length)
return yield [...r, q]
if (equal(q, t[i]))
return yield* run([...r, q], t[i], i + 1)
yield [...r, q]
yield* run([], t[i], i + 1)
}
return t.length
? Array.from(run([], t[0], 1))
: []
}
const teamAdjacent = (a, b) =>
a[1] == b[1] && a[2] == b[2] // w/l is equal
const teamSort = (a, b) =>
a[3] - b[3] // sort by sortKey, ascending
const teams =
[ ['Team A', 25, 2, 88]
, ['Team B', 20, 7, 84]
, ['Team C', 20, 7, 76]
, ['Team D', 20, 7, 54]
, ['Team E', 20, 7, 65]
, ['Team F', 20, 6, 34]
, ['Team G', 19, 8, 67]
, ['Team H', 20, 7, 11]
, ['Team N', 3, 24, 33]
]
const result =
groupAdjacent(teams, teamAdjacent)
.map(_ => _.sort(teamSort))
.flat()
console.log(result)
groupAdjacent
can be written without recursion too:
function* groupAdjacent(t, equal)
{ if (t.length === 0) return
let g = [t[0]]
for (const _ of t.slice(1))
if (equal(_, g[0]))
g.push(_)
else
(yield g, g = [_])
yield g
}
The only difference with this version is that you must wrap the groupAdjacent
call in Array.from
:
const result =
Array.from(groupAdjacent(teams, teamAdjacent))
.map(_ => _.sort(teamSort))
.flat()
// => (same output)