如何使用 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.map
和R.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
)
背景
我有一个数组数组,表示具有以下格式的网格:
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.map
和R.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
)