1
\$\begingroup\$

I have a 2D array like

[ [ 'B', 'O', 'C' ],
 [ 'J', 'B', 'F' ],
 [ 'F', 'B', 'D' ],
 [ 'F', 'D', 'W' ],
 [ 'N', 'F', 'X' ],
 [ 'X', 'J', 'F' ],
 [ 'T', 'Y', 'H' ],
 [ 'R', 'Q', 'P' ] ],

And i need to group the sub-arrays such that in the resulting 3D array each group should contain only the sub-arrays with items non identical with each other's items. Besides there shouldn't be any duplicate groups such as listing the same sub-arrays in a different order.

For the given array the result is;

[ [ [ 'B', 'O', 'C' ],
 [ 'F', 'D', 'W' ],
 [ 'T', 'Y', 'H' ],
 [ 'R', 'Q', 'P' ] ],
 [ [ 'J', 'B', 'F' ],
 [ 'T', 'Y', 'H' ],
 [ 'R', 'Q', 'P' ] ],
 [ [ 'F', 'B', 'D' ],
 [ 'T', 'Y', 'H' ],
 [ 'R', 'Q', 'P' ] ],
 [ [ 'N', 'F', 'X' ],
 [ 'T', 'Y', 'H' ],
 [ 'R', 'Q', 'P' ],
 [ 'B', 'O', 'C' ] ],
 [ [ 'X', 'J', 'F' ],
 [ 'T', 'Y', 'H' ],
 [ 'R', 'Q', 'P' ],
 [ 'B', 'O', 'C' ] ] ]

My code works fine but something back in my mind says i am doing some overkill and it can be done better. Any ideas how this job might be done any better?

var arr = [ [ 'B', 'O', 'C' ],
 [ 'J', 'B', 'F' ],
 [ 'F', 'B', 'D' ],
 [ 'F', 'D', 'W' ],
 [ 'N', 'F', 'X' ],
 [ 'X', 'J', 'F' ],
 [ 'T', 'Y', 'H' ],
 [ 'R', 'Q', 'P' ] ],
 result = arr.reduce((s,t,i,a) => t.used ? s
 : (s.push(a.map((_,j) => a[(i+j)%a.length])
 .reduce((p,c,k) => k-1 ? p.every(t => t.every(n => c.every(v => n !== v))) ? (c.used = true, p.push(c),p) : p
 : [p].every(t => t.every(n => c.every(v => n !== v))) ? (c.used = true, [p,c]) : [p])),s),[]);
 console.log(JSON.stringify(result,null,2))

The code is slightly complicated but in the heart of it lies;

.reduce((p,c,k) => k-1 ? p.every(t => t.every(n => c.every(v => n !== v))) ? (c.used = true, p.push(c),p) : p
 : [p].every(t => t.every(n => c.every(v => n !== v))) ? (c.used = true, [p,c]) : [p]))

which will take

[ [ 'B', 'O', 'C' ],
 [ 'J', 'B', 'F' ],
 [ 'F', 'B', 'D' ],
 [ 'F', 'D', 'W' ],
 [ 'N', 'F', 'X' ],
 [ 'X', 'J', 'F' ],
 [ 'T', 'Y', 'H' ],
 [ 'R', 'Q', 'P' ] ],

and turn it into

[ [ 'B', 'O', 'C' ],
 [ 'F', 'D', 'W' ],
 [ 'T', 'Y', 'H' ],
 [ 'R', 'Q', 'P' ] ]

and will mark all sub arrays as used. Then we will rotate the given array with a.map((_,j) => a[(i+j)%a.length]), up until an unused sub array is at index 0 position and repeat the same job.

asked Oct 30, 2016 at 16:24
\$\endgroup\$
5
  • \$\begingroup\$ I may be misunderstanding something, but what conceptually are you trying to do? Because at a glance, I'd think that the solution you get depends on the order of the elements of the outer array, which seems strange. That is, I start with item 1 ` (say ['B', 'O', 'C']), now I add another item which has no elements in common with item 1. there could be many such choices. but after adding that item, the next item will have 6 restrictions instead of 3, and which six depends on my choice of item 2. so your final result depends on both the order of the items and your iteration method. \$\endgroup\$ Commented Oct 30, 2016 at 22:24
  • \$\begingroup\$ @jonah OK i got your point; however whichever second sub-array, with no common elements with the sub array #1, you chose to add won't change the result. Think of it like this. Each sub-array designate a triangle and i want the disconnected ones in a group. \$\endgroup\$ Commented Oct 30, 2016 at 22:29
  • \$\begingroup\$ Consider: [[1,2], [3,4], [3,5], [5,6]. If after [1,2] I choose [3,4] then I can also choose [5,6]. If after [1,2] I choose [3,5] then I'm done. \$\endgroup\$ Commented Oct 30, 2016 at 23:01
  • \$\begingroup\$ @jonah In your test case there are multiple sets of disconnected edges and as you have figured out [[1,2], [3,4], [3,5], [5,6]] would return `[[[1,2],[3,4],[5,6]],[[1,2],[3,5]]] \$\endgroup\$ Commented Oct 30, 2016 at 23:10
  • \$\begingroup\$ ah, ok i see what you want now \$\endgroup\$ Commented Oct 30, 2016 at 23:12

1 Answer 1

1
\$\begingroup\$

Cascaded ternaries, generic variable names, obfuscated logic produces write-only and mind-boggling code that requires deciphering for anyone, including you in the future.

Let's try to write a self-explanatory code:

function groupUnique(input) {
 let results = [];
 for (let inputRow of input) {
 let collectedCells = new Set(inputRow);
 let draft = [inputRow].concat(
 input.filter(row => {
 if (row.some(cell => collectedCells.has(cell)))
 return false;
 for (let cell of row)
 collectedCells.add(cell);
 return true;
 })
 );
 let alreadyAdded = results.some(res =>
 res.length == draft.length && res.every(row => draft.includes(row))
 );
 if (!alreadyAdded)
 results.push(draft);
 }
 return results;
}
answered Oct 30, 2016 at 22:54
\$\endgroup\$
1
  • \$\begingroup\$ Thank you for the answer. I think your code does the job more or less about the same complexity. I know, i tend to use a lot of ternary and bracket operator and single letter variables. But i can perceive them as blocks of code patterns and pretty easily figure out what's going on. OK tomorrow i will test your code to see how it will perform. + for your time and efforts. \$\endgroup\$ Commented Oct 30, 2016 at 23:24

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.