如何使用 Ramda 将数组数组转换为对象数组?

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

背景

我有一个数组数组,表示具有以下格式的网格:

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

Objective

我的 objective 是将该数组转换为以下内容:

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...
];

研究

我的第一个想法是使用两个嵌套的R.mapR.chain它。但是我有一个主要问题:ramda 函数 not 将索引作为参数(与 JavaScript 中的计数器实现不同)。

所以我认为使用 ramda 做这个练习是不可能的。

问题

有没有办法只使用 ramda 函数(没有 for 循环)来做这个练习?

一个简单的嵌套循环就可以做到:

 const result = [];

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

如评论中所述:这是一个没有 ramda 的版本,但也没有任何 for 循环。

希望你会喜欢 ;)

如您所见,完全可以在没有任何 for 循环的情况下执行此操作,并且没有像 ramda 或其他库那样的任何覆盖。

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);

解决方案

使用@sjahan的解决方案使用Ramda的自然解决方案如下:

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 ) ],
  [ ]
);

使用 Ramda 的另一种方法是映射行以添加它们的列索引,然后 transpose 网格,然后再次映射它以将行索引添加到每一列。

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>

最后一次调用 R.chain 只需一步即可处理映射和展平。

请注意,这会导致列表先按列再按行升序。如果您需要按照您的示例按行然后按列升序排列的列表,您可以像这样再次转置网格:

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