5

我有一个像这样的对象:

{
    a : 'foo',
    b : 'bar',
    c : 'foo',
    d : 'baz',
    e : 'bar'
}

我想减少重复项,例如:

{
    ac : 'foo',
    be : 'bar',
    d : 'baz'
}

这样做的好方法是什么?

一些警告:

  • 只会有少量的对。(目前有 7 个;我可以想象它会上升到 20 个。)
  • 初始属性名称将永远是单个字符,如示例中所示
  • 这些值可能会达到数百个字符。
  • 速度和代码长度都非常重要,但考虑到行数很少,代码的清晰度可能仍然是最重要的。
4

6 回答 6

1

如果没有更高级的库,只需循环每对(使用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
于 2011-01-06T23:10:21.993 回答
1
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);
于 2011-01-06T23:25:49.450 回答
1

这是我能得到的最短的:

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只有在找到匹配项的情况下。不过,我决定简单一点。

于 2011-01-06T23:51:41.803 回答
0

遍历对象的每个属性并构造另一个对象,其中键是第一个的值,值是键的列表(从第一个开始)。然后你回到第二个对象并得出最终结果。

像这样的东西:

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 个左右的属性),您可以编写 n 2版本,在其中您只需迭代属性,然后为每个属性再次迭代。但是,如果您的对象可能很大并且您必须经常执行此操作,那可能不是一个好主意。

于 2011-01-06T23:09:11.527 回答
0

如果我完全出局了,请原谅我,但在我看来,你组合这些的方式,你得到了错误的键和值。那这个呢?

{
    '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);
    }
}

(大脑编译;可能有几个错误。)

于 2011-01-07T09:23:05.640 回答
0

从使用字典以翻转方式计算标签的 reduce 开始。非常高效的方式,因为它使用内置的字典支持,没有 for 循环等。

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"}
于 2015-07-18T12:57:31.363 回答