lodash/underscore 计算数组中非唯一元素的数量

lodash/underscore find the number of non-unique elements in an array

我有一个包含一些字符串的数组,我需要找出有多少字符串不是唯一的 - _.uniq 相反。我已经尝试了一些事情,但到目前为止都遇到了死胡同。我觉得答案很简单。举个例子:

["abc", "abc", "def", "rty", "rty", "rty", "uig", "ghe", "bed", "abc"]

我想从中得到答案 2,因为数组中只有两个字符串不止一次出现。

这是 underscore 的一种方法。您只需继续在累加器上搜索重复值并将非唯一项添加到另一个 object/array。注意:这仅适用于字符串数组。

var list = ["eyxCyqqbn3", "jPEbM00mvQ", "5pi7wTh689", "P2iBGGwuWZ", "eyxCyqqbn3", "jPEbM00mvQ", "uX1GrD4UQt", "uX1GrD4UQt", "uX1GrD4UQt", "uX1GrD4UQt", "uX1GrD4UQt", "yXkmkZo0lW", "5pi7wTh689", "P2iBGGwuWZ", "uX1GrD4UQt", '__proto__', '__proto__'];
var repeated = Object.create(null);
_.reduce(list, function(acc, val) {
  if (Object.prototype.hasOwnProperty.call(acc, val)) {
    if (!Object.prototype.hasOwnProperty.call(repeated, val)) {
      repeated[val] = true;
    }
  }
  acc[val] = true;
  return acc;
}, Object.create(null));

var amountrepeated = Object.keys(repeated).length;

document.getElementById('results').innerHTML = amountrepeated;
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<pre id="results"></pre>

var list = ["abc", "abc", "def", "rty", "rty", "rty", "uig", "ghe", "bed", "abc"];

var repeatedCount = _.filter(_.groupBy(list), function(n) { return n.length > 1; }).length;

所以您正在寻找一种廉价的跨浏览器解决方案来解决您的问题?通过使用专为 ES3+ 设计的下划线或 lodash。但是,您必须小心列表中的字符串。

我们来看看MinusFour的回答

var $countOfLikeStrings3 = function(arrayOfStrings) {
  var repeated = {
    length: 0
  };

  _.reduce(arrayOfStrings, function(acc, val) {
    if (acc[val] !== undefined) {
      if (repeated[val] === undefined) {
        repeated[val] = true;
        repeated.length++;
      }
    }

    acc[val] = true;

    return acc;
  }, {});

  return repeated.length;
}

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var list1 = ['length', 'length', 'constructor', 'constructor', 'prototype', 'prototype', '__proto__', '__proto__'];

log($countOfLikeStrings3(list1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<pre id="out"></pre>

我猜我们没想到结果是 1?

一个现在的阿奎那解决方案。

var $countOfLikeStrings2 = function(arrayOfStrings) {
  return _.filter(_.groupBy(arrayOfStrings), function(n) {
    return n.length > 1;
  }).length;
}

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var list1 = ['length', 'length', 'constructor', 'constructor', 'prototype', 'prototype', '__proto__', '__proto__'];

log($countOfLikeStrings2(list1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<pre id="out"></pre>

再说一遍,没想到是 3?

好的,现在让我们使用一些现代的 ES6,这样浏览器支持会减少,但这将适用于当前的主流浏览器和节点。

var $countOfLikeStrings1 = (function(create) {
  'use strict';

  var symRepeated = Symbol('repeated'),
    symLength = Symbol('length');

  return function(arrayOfStrings) {
    var accumulator = create(null);

    accumulator[symRepeated] = create(null);
    accumulator[symRepeated][symLength] = 0;

    return arrayOfStrings.reduce(function(acc, stringItem) {
      var repeated = acc[symRepeated],
        count = (acc[stringItem] || 0) + 1;

      acc[stringItem] = count;
      if (count > 1) {
        if (!repeated[stringItem]) {
          repeated[symLength] += 1;
        }

        repeated[stringItem] = count;
      }

      return acc;
    }, accumulator)[symRepeated][symLength];
  };
}(Object.create));

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var list1 = ['length', 'length', 'constructor', 'constructor', 'prototype', 'prototype', '__proto__', '__proto__'];

log($countOfLikeStrings1(list1));
<pre id="out"></pre>

嘿!差不多了,4!

所以,在 ES6 中甚至可能有更好的方法——我仍在学习新的东西。并且通过一些额外的 ES3 和 ES5 代码,其他两个解决方案也可以得到改进。

抛开这些问题,这里是 jsPerf 这 3 个解决方案的原样。

更新:重新设计为更加 ES6

function $countOfLikeStrings4(arrayOfStrings) {
  'use strict';

  const accumulator = new Map(),
    repeated = new Map();

  var oldCount,
    newCount;

  for (let stringItem of arrayOfStrings) {
    oldCount = accumulator.get(stringItem) || 0;
    newCount = oldCount + 1;
    accumulator.set(stringItem, newCount);
    if (newCount > 1 && !repeated.has(stringItem)) {
      repeated.set(stringItem, true);
    }
  }

  return repeated.size;
}

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var list1 = ['length', 'length', 'constructor', 'constructor', 'prototype', 'prototype', '__proto__', '__proto__'];

log($countOfLikeStrings4(list1));
<pre id="out"></pre>