Search code examples
arraysecmascript-6countingramda.js

Ramda: Find lowest occasions of one array in another array


I have two arrays containing strings. The second array can only contain strings, that are in the first, but multiple times. I'm trying to find the words, with the lowest occasions in the second array. Here are some examples:

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

How would you do this with Ramda? (or in general in JS)? I feel like it can be solved with R.countBy but then I don't know how to get all the keys with the lowest values.


Solution

  • You can use R.countBy, but you'll have to create a defaults object, where each item in the originals has the value of 0, and then merge them. Afterwards, find the minimum value, and filter the original array:

    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>