转换键值对数组:值的键和唯一键的值
Convert array of keys-value pairs: keys for values, and values for unique keys
我想转换用作键值存储的数组数组。
每个子数组采用以下形式:['tom',['hello','world']]
,其中 [0]
索引 ('tom'
) 是 'key',[1]
索引(数组)是 'value'.
我希望我的数组中的所有 'values' 都是新数组的唯一键,并且我数组中的键应该构造新的子数组,其中包含保存相应值的所有先前键.
例如:
var myArray = [
['tom',['hello','world']],
['bob',['world','foo']],
['jim',['foo','bar']]
];
以上输入应该得到如下输出:
var newArray = [
['hello',['tom']],
['world',['tom','bob']],
['foo',['bob','jim']],
['bar',['jim']],
];
我怎样才能做到这一点?
在开始 'solution' 之前,我想说明这样一个事实,即这种存储和描述数据的方式非常糟糕。如果您正在寻找类似于 PHP
中的关联数组的内容,您应该学习如何 work with objects.
JS 中的对象只是唯一键(属性)-> 值对的集合。
作为对象,您的数据集将如下所示:
var before = {
tom: ['hello','world'],
dick: ['world','foo'],
harry: ['foo','bar']
};
var after = {
bar: ["harry"],
foo: ["dick", "harry"],
hello: ["tom"],
world: ["tom", "dick"]
};
这是一个使用对象的实现。也很幼稚,但是简单多了。
var before = {
tom: ['hello','world'],
dick: ['world','foo'],
harry: ['foo','bar']
};
var after = {
bar: ["harry"],
foo: ["dick", "harry"],
hello: ["tom"],
world: ["tom", "dick"]
};
function resObj(obj) {
var o = {};
for (var k in obj) {
for (var i = 0; i < obj[k].length; i++) {
o[obj[k][i]] = o[obj[k][i]] || [];
o[obj[k][i]].push(k);
}
}
return o;
}
console.log('Expected:', after);
console.log('Actual:', resObj(before));
下面是一个示例,说明如何使用数组执行您想要的操作。它幼稚、缓慢,我相信它可以改进,但这与我的回答无关。
粗略的演示。注意,我们用一个对象作为交换,使得它和上面几乎一样。
var inp = [
['tom',['hello','world']],
['dick',['world','foo']],
['harry',['foo','bar']]
];
var out = [
['hello',['tom']],
['world',['tom','dick']],
['foo',['dick','harry']],
['bar',['harry']]
];
function resArray(arr) {
var q = {},
o = [];
for(var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr[i][1].length; j++) {
q[arr[i][1][j]] = q[arr[i][1][j]] || [];
q[arr[i][1][j]].push(arr[i][0]);
}
}
for (var m in q) {
o.push([m, q[m]]);
}
return o;
}
console.log('Expected:', out);
console.log('Actual:', resArray(inp));
就像我在评论中所说的那样,您实际上是在要求计算多对一关系的倒数。您可以将关系视为一对对象的映射。从逻辑上讲,您正在将 "tom" 映射到 "hello" 和 "world"。反向关系将 "hello" 和 "world" 映射到 "tom".
当您想到"relations"时,您应该想到关联容器而不是数组。使用数组会使你的算法效率低得多,除非你的键是密集的整数。
这会产生正确的输出:
var myRelation = [
['tom', ['hello', 'world']],
['dick', ['world', 'foo']],
['harry', ['foo', 'bar']]
];
function inverse(relation) {
// This first half does the hard work of computing the inverse.
var intermediate = {};
relation.forEach(function(outerEntry) {
outerEntry[1].forEach(function(innerEntry) {
if (!intermediate[innerEntry]) {
intermediate[innerEntry] = {};
}
intermediate[innerEntry][outerEntry[0]] = true;
});
});
// This second half turns the intermediate assocative container
// back into an array of nested arrays.
var output = [];
Object.keys(intermediate).forEach(function(outerEntry) {
output.push([outerEntry, []]);
Object.keys(intermediate[outerEntry]).forEach(function(innerEntry) {
output[output.length - 1][1].push(innerEntry);
});
});
return output;
}
console.log(inverse(myRelation));
如果您不再需要在数组中重现原始输出格式,问题就会变得简单一些。
这里是您转换函数的一个干净的实现。
/**
* Converts from a one-to-many to many-to-one relationship.
* @param {[array]} pairs [array representing a one-to-many relationship.]
* @return {[array]} [array representing the same data, in a many-to-one relationship]
*/
var oneToManyFlip = function(pairs){
//Recall that current[0] is our 'key'.
//Also recall that 'prev' is the empty object we passed in.
var result = pairs.reduce(function(storage, current, index, array){
current[1].forEach(function(element, index){
if(storage[element] === undefined){ storage[element] = [];}
storage[element].push(current[0]);
});
return storage;
}, {});
return Object.keys(result).map(function(element){
return [element, result[element]];
});
}
我希望它更简洁、更易于推理。 Map & Reduce 一开始可能有点棘手。
您可以了解有关函数式 JS 的更多信息here。它对可迭代对象的这类计算很有用。
我想转换用作键值存储的数组数组。
每个子数组采用以下形式:['tom',['hello','world']]
,其中 [0]
索引 ('tom'
) 是 'key',[1]
索引(数组)是 'value'.
我希望我的数组中的所有 'values' 都是新数组的唯一键,并且我数组中的键应该构造新的子数组,其中包含保存相应值的所有先前键.
例如:
var myArray = [
['tom',['hello','world']],
['bob',['world','foo']],
['jim',['foo','bar']]
];
以上输入应该得到如下输出:
var newArray = [
['hello',['tom']],
['world',['tom','bob']],
['foo',['bob','jim']],
['bar',['jim']],
];
我怎样才能做到这一点?
在开始 'solution' 之前,我想说明这样一个事实,即这种存储和描述数据的方式非常糟糕。如果您正在寻找类似于 PHP
中的关联数组的内容,您应该学习如何 work with objects.
JS 中的对象只是唯一键(属性)-> 值对的集合。
作为对象,您的数据集将如下所示:
var before = {
tom: ['hello','world'],
dick: ['world','foo'],
harry: ['foo','bar']
};
var after = {
bar: ["harry"],
foo: ["dick", "harry"],
hello: ["tom"],
world: ["tom", "dick"]
};
这是一个使用对象的实现。也很幼稚,但是简单多了。
var before = {
tom: ['hello','world'],
dick: ['world','foo'],
harry: ['foo','bar']
};
var after = {
bar: ["harry"],
foo: ["dick", "harry"],
hello: ["tom"],
world: ["tom", "dick"]
};
function resObj(obj) {
var o = {};
for (var k in obj) {
for (var i = 0; i < obj[k].length; i++) {
o[obj[k][i]] = o[obj[k][i]] || [];
o[obj[k][i]].push(k);
}
}
return o;
}
console.log('Expected:', after);
console.log('Actual:', resObj(before));
下面是一个示例,说明如何使用数组执行您想要的操作。它幼稚、缓慢,我相信它可以改进,但这与我的回答无关。
粗略的演示。注意,我们用一个对象作为交换,使得它和上面几乎一样。
var inp = [
['tom',['hello','world']],
['dick',['world','foo']],
['harry',['foo','bar']]
];
var out = [
['hello',['tom']],
['world',['tom','dick']],
['foo',['dick','harry']],
['bar',['harry']]
];
function resArray(arr) {
var q = {},
o = [];
for(var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr[i][1].length; j++) {
q[arr[i][1][j]] = q[arr[i][1][j]] || [];
q[arr[i][1][j]].push(arr[i][0]);
}
}
for (var m in q) {
o.push([m, q[m]]);
}
return o;
}
console.log('Expected:', out);
console.log('Actual:', resArray(inp));
就像我在评论中所说的那样,您实际上是在要求计算多对一关系的倒数。您可以将关系视为一对对象的映射。从逻辑上讲,您正在将 "tom" 映射到 "hello" 和 "world"。反向关系将 "hello" 和 "world" 映射到 "tom".
当您想到"relations"时,您应该想到关联容器而不是数组。使用数组会使你的算法效率低得多,除非你的键是密集的整数。
这会产生正确的输出:
var myRelation = [
['tom', ['hello', 'world']],
['dick', ['world', 'foo']],
['harry', ['foo', 'bar']]
];
function inverse(relation) {
// This first half does the hard work of computing the inverse.
var intermediate = {};
relation.forEach(function(outerEntry) {
outerEntry[1].forEach(function(innerEntry) {
if (!intermediate[innerEntry]) {
intermediate[innerEntry] = {};
}
intermediate[innerEntry][outerEntry[0]] = true;
});
});
// This second half turns the intermediate assocative container
// back into an array of nested arrays.
var output = [];
Object.keys(intermediate).forEach(function(outerEntry) {
output.push([outerEntry, []]);
Object.keys(intermediate[outerEntry]).forEach(function(innerEntry) {
output[output.length - 1][1].push(innerEntry);
});
});
return output;
}
console.log(inverse(myRelation));
如果您不再需要在数组中重现原始输出格式,问题就会变得简单一些。
这里是您转换函数的一个干净的实现。
/**
* Converts from a one-to-many to many-to-one relationship.
* @param {[array]} pairs [array representing a one-to-many relationship.]
* @return {[array]} [array representing the same data, in a many-to-one relationship]
*/
var oneToManyFlip = function(pairs){
//Recall that current[0] is our 'key'.
//Also recall that 'prev' is the empty object we passed in.
var result = pairs.reduce(function(storage, current, index, array){
current[1].forEach(function(element, index){
if(storage[element] === undefined){ storage[element] = [];}
storage[element].push(current[0]);
});
return storage;
}, {});
return Object.keys(result).map(function(element){
return [element, result[element]];
});
}
我希望它更简洁、更易于推理。 Map & Reduce 一开始可能有点棘手。
您可以了解有关函数式 JS 的更多信息here。它对可迭代对象的这类计算很有用。