我正在使用lodash处理Node / JS,并且我正在尝试将数组数组转换为哈希对象,以便:
[ [ 'uk', 'london', 'british museum' ],
[ 'uk', 'london', 'tate modern' ],
[ 'uk', 'cambridge', 'fitzwilliam museum' ],
[ 'russia', 'moscow', 'tretyakovskaya gallery' ],
[ 'russia', 'st. petersburg', 'hermitage' ],
[ 'russia', 'st. petersburg', 'winter palace' ],
[ 'russia', 'st. petersburg', 'russian museum' ] ]
成为这种哈希/树结构:
{ uk: {
london: [ 'british museum', 'tate modern' ],
cambridge: [ 'fitzwilliam museum' ]
},
russia: {
moscow: [ 'tretyakovskaya gallery' ],
'st petersburg': ['hermitage', 'winter palace', 'russian museum']
}
}
到目前为止,我已经使用过这种代码:
function recTree(arr) {
// We only take in arrays of arrays
if (arr.constructor === Array && arr[0].constructor === Array) {
// If array has a single element, return it
if (arr.length === 1) {
return arr[0];
}
// Group array by first element
let grouped = _.groupBy(arr, function(o) {
return o[0]
});
let clean = _.mapValues(grouped, function(o) {
return _.map(o, function(n) {
// Cut off first element
let tail = n.slice(1);
if (tail.constructor === Array && tail.length == 1) {
// If there is a single element, return it
return tail[0];
} else {
return tail;
}
});
});
return _.mapValues(clean, recTree)
} else {
// If it's not an array of arrays, return it
return arr;
}
}
我想知道是否有比我迄今为止编程的更清洁,更实用的方法。理想情况下,我希望能够接受任意(但是常量,所有内部数组都相同)长度(不仅仅是3)数组的数组。
答案 0 :(得分:3)
这是一个适用于任何长度可变数组的lodash解决方案。
使用lodash#reduce
将数组缩减为object
格式。
在每次reduce迭代中:
2.1。我们得到了我们想要设置的值的path
,例如uk.london
,使用lodash#initial
。
2.2。使用lodash#last
,从我们想要连接的内部数组中获取值。
2.3使用lodash#get
使用object
从path
获取任何现有数组,如果它没有获得任何值,则默认为空数组。获取值后,我们将内部数组的最后一项连接到获得的值。
2.4使用lodash#set
使用取自2.1的object
从value
中取得path
的结果var result = _.reduce(array, function(object, item) {
var path = _.initial(item);
var value = _.get(object, path, []).concat(_.last(item));
return _.set(object, path, value);
}, {});
。
var array = [
['uk', 'london', 'british museum'],
['uk', 'london', 'tate modern'],
['uk', 'cambridge', 'fitzwilliam museum'],
['russia', 'moscow', 'tretyakovskaya gallery'],
['russia', 'st. petersburg', 'hermitage'],
['russia', 'st. petersburg', 'winter palace'],
['russia', 'st. petersburg', 'russian museum']
];
var result = _.reduce(array, function(object, item) {
var path = _.initial(item);
var value = _.get(object, path, []).concat(_.last(item));
return _.set(object, path, value);
}, {});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
{{1}}
答案 1 :(得分:2)
您可以使用Array#reduce
创建/访问嵌套数据结构并推送数组的最后一个元素。
编辑:此解决方案适用于任意长度的内部数组。
var array = [['foo', 'bar', 'baz', 42], ['uk', 'london', 'british museum'], ['uk', 'london', 'tate modern'], ['uk', 'cambridge', 'fitzwilliam museum'], ['russia', 'moscow', 'tretyakovskaya gallery'], ['russia', 'st. petersburg', 'hermitage'], ['russia', 'st. petersburg', 'winter palace'], ['russia', 'st. petersburg', 'russian museum']],
object = {};
array.forEach(function (a) {
a.slice(0, -1).reduce(function (o, k, i, kk) {
return o[k] = o[k] || kk.length - 1 - i && {} || [];
}, object).push(a[a.length - 1]);
});
console.log(object);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
ES6
var array = [['foo', 'bar', 'baz', 42], ['uk', 'london', 'british museum'], ['uk', 'london', 'tate modern'], ['uk', 'cambridge', 'fitzwilliam museum'], ['russia', 'moscow', 'tretyakovskaya gallery'], ['russia', 'st. petersburg', 'hermitage'], ['russia', 'st. petersburg', 'winter palace'], ['russia', 'st. petersburg', 'russian museum']],
object = {};
array.forEach(
a => a
.slice(0, -1)
.reduce((o, k, i, kk) => o[k] = o[k] || kk.length - 1 - i && {} || [], object)
.push(a[a.length - 1])
);
console.log(object);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
答案 2 :(得分:1)
由于功能和使用lodash不同,在此解决方案中,内部数组可以是可变长度的:
var arrayOfArrays = [ [ 'uk', 'london', 'british museum' ], [ 'uk', 'london', 'tate modern' ], [ 'uk', 'cambridge', 'fitzwilliam museum' ], [ 'russia', 'moscow', 'tretyakovskaya gallery' ], [ 'russia', 'st. petersburg', 'hermitage' ], [ 'russia', 'st. petersburg', 'winter palace' ], [ 'russia', 'st. petersburg', 'russian museum' ] ]
var resultingObject = arrayOfArrays.reduce(function(obj, arr) {
arr.reduce(function(parent, value, i) {
if (i < arr.length - 1) {
parent[value] = parent[value] || (i < arr.length - 2 ? {} : []);
return parent[value];
}
parent.push(value);
}, obj);
return obj;
}, {});
console.log(resultingObject);
答案 3 :(得分:-1)
分组和映射可能不是解决问题的更有效方法。
一种更好的方法,因为您只需一次详细说明数据,就应该在阅读清单时创建地图。
如果不满足先决条件,代码更容易理解,我会尽快摆脱这个功能。
我不会考虑的另一个问题是返回第一个元素 如果列表的长度只有一个,则为输入数组:如果给定正确的输入格式,任何基数,则应返回相同的输出类型。
你的函数返回三个不同的函数:
如果列表未被识别(您已经有输入)或者在所有其他情况下返回带有映射的对象,我会抛出异常
var x = [
[ 'uk', 'london', 'british museum' ],
[ 'uk', 'london', 'tate modern' ],
[ 'uk', 'cambridge', 'fitzwilliam museum' ],
[ 'russia', 'moscow', 'tretyakovskaya gallery' ],
[ 'russia', 'st. petersburg', 'hermitage' ],
[ 'russia', 'st. petersburg', 'winter palace' ],
[ 'russia', 'st. petersburg', 'russian museum' ]
];
/*
* function to add a single point of interest at the
* right place in the map
*
*/
function remap(map, location) {
var state, city, poi;
[state, city] = location;
poi = location.slice(2);
if (!map[state]) {
// create a new state property
map[state]={};
}
if (!map[state][city]) {
// first time we encounter a city: create its slot
map[state][city]=[];
}
// Add the points of interest not in the list
map[state][city].concat(
poi.filter(function(p) {
return !map[state][city].includes(p);
})
);
return map;
}
function recTree(arr) {
// We only take in arrays of arrays
// if not, better to throw an exception!
if (arr.constructor !== Array || arr[0].constructor !== Array) {
throw "recTree: invalid parameter, we need an array of array";
}
return x.reduce(remap, {});
}
如果你真的需要一个_lodash版本,你可以从这个
开始function recTree(arr) {
// We only take in arrays of arrays
// if not, better to throw an exception!
if (arr.constructor !== Array || arr[0].constructor !== Array) {
throw "recTree: invalid parameter, we need an array of array";
}
return _.reduce(x, remap, {});
}
在这两种情况下,您都可以使用以下方法获取数据(try catch enclosure是可选的):
try {
// if you really trust your code use just the following line
var mymap = recTree(arr);
} catch(e) {
// do anything you can from recover for the error or rethrow to alert
// developers that something was wrong with your input
}