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.

\$\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\$
    – Jonah
    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\$
    – Redu
    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\$
    – Jonah
    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\$
    – Redu
    Commented Oct 30, 2016 at 23:10
  • \$\begingroup\$ ah, ok i see what you want now \$\endgroup\$
    – Jonah
    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;
}
\$\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\$
    – Redu
    Commented Oct 30, 2016 at 23:24

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.