Ramda:在另一个数组中找到一个数组的最低出现次数

Ramda: Find lowest occasions of one array in another array

我有两个包含字符串的数组。第二个数组只能包含第一个但多次出现的字符串。我试图找到第二个数组中出现次数最少的单词。以下是一些示例:

const original = ['foo', 'bar', 'baz'];

const arr1 = ['foo', 'bar']; // => ['baz'] because 'baz' is 0 times in arr1.
const arr2 = ['foo', 'foo', 'bar']; // => ['baz'] because 'baz' is 0 times in arr2.
const arr3 = ['foo', 'foo', 'bar', 'bar', 'baz']; // => ['baz'] because 'baz' in 1 time in arr3.
const arr4 = ['foo', 'bar', 'baz']; // => ['foo', 'bar', 'baz']; because they are all the lowest (1 time).
const arr5 = ['foo', 'foo', 'bar', 'bar', 'bar', 'baz', 'baz']; // => ['foo', 'baz'] because they are both only 2 times in arr5

你会如何用 Ramda 做这个? (或一般在 JS 中)?我觉得它可以用 R.countBy 解决,但我不知道如何获得所有具有最低值的键。

您可以使用R.countBy,但您必须创建一个默认对象,其中originals 中的每个项目的值为0,然后合并它们。然后求最小值,过滤original数组:

const { chain, flip, zipObj, map, always, curry, pipe, countBy, identity, mergeRight, values } = R;

const getDefaults = chain(flip(zipObj), map(always(0)));

const countWithDefaults = curry((dflts, arr) => pipe(
  countBy(identity),
  mergeRight(dflts),
)(arr));

const fn = curry((orig, arr) => {
  const counts = countWithDefaults(getDefaults(orig), arr);
  
  const min = Math.min(...values(counts));

  return original.filter(k => counts[k] === min);
});

const original = ['foo', 'bar', 'baz'];

const fnO = fn(original);

console.log(fnO(['foo', 'bar'])); // => ['baz'] because 'baz' is 0 times in arr1.
console.log(fnO(['foo', 'foo', 'bar'])); // => ['baz'] because 'baz' is 0 times in arr2.
console.log(fnO(['foo', 'foo', 'bar', 'bar', 'baz'])); // => ['baz'] because 'baz' in 1 time in arr3.
console.log(fnO(['foo', 'bar', 'baz'])); // => ['foo', 'bar', 'baz']; because they are all the lowest (1 time).
console.log(fnO(['foo', 'foo', 'bar', 'bar', 'bar', 'baz', 'baz'])); // => ['foo', 'baz'] because they are both only 2 times in arr5
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>

纯 ES6 版本还不错:

const minOccurences = (orig, xs) => 
  Object .entries (xs .reduce (
    (a, x) => ({... a, [x]: (a [x] || 0) + 1}),
    Object .assign (... orig .map (x => ({[x]: 0})))
  )).reduce(
    ({vs, m}, [v, c]) => c < m ? {vs: [v], m: c} : c == m ? {vs: [...vs, v], m} : {vs, m},
    {vs: [], m: Infinity}
  ).vs

const original = ['foo', 'bar', 'baz'];

console.log (minOccurences(original, ['foo', 'bar'])); // => ['baz'] because 'baz' is 0 times in arr1.
console.log (minOccurences(original, ['foo', 'foo', 'bar'])); // => ['baz'] because 'baz' is 0 times in arr2.
console.log (minOccurences(original, ['foo', 'foo', 'bar', 'bar', 'baz'])); // => ['baz'] because 'baz' in 1 time in arr3.
console.log (minOccurences(original, ['foo', 'bar', 'baz'])); // => ['foo', 'bar', 'baz']; because they are all the lowest (1 time).
console.log (minOccurences(original, ['foo', 'foo', 'bar', 'bar', 'bar', 'baz', 'baz'])); // => ['foo', 'baz'] because they are both only 2 times in arr5
.as-console-wrapper {min-height: 100% !important; top: 0}

我们首先在 Obect.assign 调用中从原始数组创建一个基本计数对象 {foo: 0, bar: 0, baz: 0}。我们将其作为初始值输入到第一个 reduce 调用中,将第二个数组折叠成 {foo: 2, bar: 3, baz: 2} 之类的东西。然后我们的第二个 reduce 调用将它折叠成类似 {vs: ['foo', 'baz'], m: 2} 的东西,其中 m 是最小计数,vs 具有该计数的项目。然后我们只需从结果中提取 vs 即可得到答案。