题
我有一个类似的对象:
{
a : 'foo',
b : 'bar',
c : 'foo',
d : 'baz',
e : 'bar'
}
我想减少重复项,例如:
{
ac : 'foo',
be : 'bar',
d : 'baz'
}
这是什么好方法?
一些警告:
- 只有少数对。 (目前有7个;我可以想象它会增加20。)
- 最初的属性名称只会像示例一样是一个字符
- 这些值可能会运行几百个字符。
- 速度和代码长度都非常重要,但是鉴于少数行,代码清晰度可能仍然是最重要的。
解决方案
浏览对象的每个属性并构造 其他 对象,其中键是第一个值,而值是键列表(从第一个)。然后,您返回第二个对象并最终结果。
这样的事情:
function noDupes(obj) {
var o2 = {};
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
var list = o2[obj[k]] || [];
list.push(k);
o2[obj[k]] = list;
}
}
var rv = {};
for (k in o2) {
if (o2.hasOwnProperty(k))
rv[o2[k].join('')] = k;
}
return rv;
}
现在,如果原始对象的值不是字符串,那么事情就会越来越涉及:只有字符串可以是JavaScript对象中的属性键。在这种情况下,您可以四处寻找更一般的哈希实现。如果您的对象往往很小(少于10个左右的属性),则可以写入N2 版本,您简单地在属性上迭代,然后再次迭代每个属性。但是,如果您的对象可能很大,并且您必须经常执行此操作,那可能是个坏主意。
其他提示
var Reduce = function(obj)
{
var temp = {};
var val = "";
for (var prop in obj)
{
val = obj[prop];
if (temp[val])
temp[val] = temp[val] + prop.toString();
else
temp[val] = prop.toString();
}
var temp2 = {};
for (var prop in temp)
{
val = temp[prop];
temp2[val] = prop.toString();
}
return temp2;
};
用于:
var obj = {
a :"foo",
b : "bar",
c : "foo",
d : "bar",
e : "bar"
};
var ob2 = Reduce(obj);
这是我可以理解的最短:
var obj, newObj = {}; // obj is your original
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
for (var j in newObj) {
if (newObj.hasOwnProperty(j) && newObj[j] === obj[i]) break;
j = "";
}
newObj[i + j] = obj[i];
j && delete newObj[j];
}
解释:
- 它通过原始对象中的每个项目循环,
obj
, ,并产生一个新对象,newObj
. - 对于原始项目中的每个项目,它搜索了半生产的
newObj
对于相同的值。 - 结果是j
, ,如果找到该属性的名称,则要么是空字符串(如果不是)。 - 无论哪种情况,新对象都需要与原始对象中当前属性相同名称的属性,以及此值
j
. - 它还删除了发现的属性
newObj
如果有一个,以防止重复构建。
诚然,设置 j = ""
在循环中效率低下。可以轻松地用第二个变量集替换为 ""
最初, j
只有找到比赛。我决定为简单起见。
没有更高的库,只需循环每对 hasOwnProperty
)并将键添加到直方图中,其中直方图键是对值,而直方图值为串联键。然后反转直方图的密钥/值。
编辑: 如果初始值不是字符串(并且不要可逆地映射),则现有的“身份哈希”库仍然可以启用上述工作方法。
或者,您可以映射说, [[k,v],...]
然后进行排序,然后使用类似于 水桶排序 (想象一下已经排序)在输出通过中合并“相等键”的值。
它可能会像这样(虽然代码可能具有错误,但该方法是正确的 - 只要您有一种比较值的方法,它也将与任意对象一起使用:
var _f = []
for (var k in map) {
if (map.hasOwnProperty(k)) {
_f.push({k: k, v: map[k]})
}
}
// you could also sort on name (a.k), if it's important
// this makes it more versatile and deterministic in output
// ordering than the histogram method above
var f = _f.sort(function (a, b) { return a.v < b.v ? 1 : a.v > b.v ? -1 : 0 })
var res = {}
var prev
var name = ""
// after the sort all {k:,v:} objects with the same values will be grouped
// together so we only need to detect the change to the next value
// and everything prior to that gets the merged key
for (var i = 0; i < f.length; i++) {
var p = f[i]
if (prev != p.v && name) {
res[name] = prev
name = ""
} else {
name = name + p.k
}
prev = p.v
}
if (name) { // don't forget last set of values
res[name] = prev
}
// have res
如果我完全出去,请原谅我,但在我看来,您将它们结合在一起,您的钥匙和价值观都以错误的方式。那这个呢?
{
'foo': ['a', 'c'],
'bar': ['b', 'e'],
'baz': ['d']
}
应该很容易转换:
flippedObj = {};
for (var letter in obj) {
if (obj.hasOwnProperty(letter)) {
var letters = flippedObj[obj[letter]];
if (letters === undefined) {
letters = [];
flippedObj[obj[letter]] = letters;
}
letters.push(letter);
}
}
(大脑编译;可能有几个错误。)
首先使用使用字典以翻转方式计数标签的简化开始。非常性能的方式,因为它使用内置的字典支持,无用于循环等。
var flipped = Object.keys(input).reduce(function(a,b){
var tag = input[b];
a[tag] = (a[tag] || '') + b;
return a;
}, {});
返回具有翻转格式的对象:
// {foo: "ac", bar: "be", baz: "d"}
然后只需翻转格式:
Object.keys(flipped).reduce(function(a,b){
a[flipped[b]]=b;
return a;
}, {});
输出:
// {ac: "foo", be: "bar", d: "baz"}