简体   繁体   中英

How to transform array of arrays into an array of objects with Ramda?

Background

I have an array of arrays that represents a grid with the following format:

const grid = [
    [null, null, null, null, null, null, null],
    [null, null, null, null, null, null, null],
    [null, "®" , null, null, null, null, "®" ],
    ["©" , "©" , null, null, "©" , "®" , "®" ],
    ["®" , "®" , "®" , "©" , "®" , "©" , "©" ],
    ["®" , "©" , "©" , "®" , "©" , "®" , "©" ],
];

Objective

My objective is to transform that array into the following:

const grid = [ 
    { row: 0, column: 1, value: null},
    { row: 0, column: 2, value: null},
    { row: 0, column: 3, value: null},
    //... some rows after ...
    { row: 3, column: 0, value: "©"},
    // etc...
];

Research

My first idea was to use two nested R.map and the R.chain it. However I have a major problem: The ramda functions do not take indexes as parameters ( unlike the counter implementations in JavaScript ).

So I believe that doing this exercise using ramda, is not possible.

Question

Is there a way to do this exercise using only ramda functions ( no for loops ) ?

As discussed in comments: here is a version without ramda, but also without any for loop.

Hope you'll like it ;)

As you can see, it's totally possible to do this without any for loop, and without any overlay like ramda or another lib.

 const grid = [ [null, null, null, null, null, null, null], [null, null, null, null, null, null, null], [null, "®" , null, null, null, null, "®" ], ["©" , "©" , null, null, "©" , "®" , "®" ], ["®" , "®" , "®" , "©" , "®" , "©" , "©" ], ["®" , "©" , "©" , "®" , "©" , "®" , "©" ], ]; const result = grid.reduce((r, array, i) => [...r, ...array.map((value, j) => ({row: i, column: j, value}))], []); console.log(result); 

Solution

Using the solution from @sjahan The natural solution using Ramda is as follow:

const R  require("ramda");
const mapIndexed = R.addIndex( R.map );
const reduceIndexed = R.addIndex( R.reduce );

const convertGrid = reduceIndexed(
  ( acc, array, i ) => [...acc, ...mapIndexed( ( value, j ) => ( { row: i, column: j, value } ), array ) ],
  [ ]
);

A different approach using Ramda is to map over the rows to add their column index, then transpose the grid, then map over it again to add row index to each column.

 const grid = [ [null, null, null, null, null, null, null], [null, null, null, null, null, null, null], [null, "®" , null, null, null, null, "®" ], ["©" , "©" , null, null, "©" , "®" , "®" ], ["®" , "®" , "®" , "©" , "®" , "©" , "©" ], ["®" , "©" , "©" , "®" , "©" , "®" , "©" ], ]; const mapCol = R.addIndex(R.map)(R.flip(R.assoc('column'))) const mapRow = R.addIndex(R.map)(R.flip(R.assoc('row'))) const mapVal = R.map(R.objOf('value')) const fn = R.pipe( R.map(mapVal), // convert each value to `{ value: ... }` R.map(mapCol), // add the column index `{ value: ..., column: n }` R.transpose, // transpose the whole grid R.chain(mapRow) // add the row index `{ value: ..., column: n, row: m }` ) console.log(fn(grid)) 
 <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script> 

The last call to R.chain just takes care of mapping and flattening in a single step.

Note that this results in a list ascending by column then row. If you need the list ascending by row then column as per your example, you could transpose the grid again like so:

const fn = pipe(
  R.map(mapVal),
  R.map(mapCol),
  R.transpose,
  R.map(mapRow),
  R.transpose,
  R.unnest
)

A simple nested loop does it:

 const result = [];

 for(const [row, content] of grid.entries()) {
    for(const [column, value] of content.entries()) {
       result.push({ row, column, value });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM