How do I select the value with the lowest "count" in a Javascript object

Viewed 566

Through the use of lodash's _.countBy function, I have generated a tally object, counting the number of times a value occurs in an array.

What would be the best method to select the lowest count in the "value -> count" object?

 var/input = ["A","A","B","B","C"];
 var/tally = _.countBy(input); // Tally is {"A":2, "B":2, "C":1}

 // [...solution...]

 Console.assert(lowest == "C");

No special handling of ties is required, provided it's tied for the lowest count. Lodash is available.

6 Answers

You could reduce the array of keys and get the smallest key by checking the value.

var tally = { A: 2, B: 2, C: 1 },
    lowest = Object.keys(tally).reduce((a, b) => tally[a] < tally[b] ? a : b);

console.log(lowest);

A lodash approach by pairing the properties and getting the minimum with at index 1 and taking the first element of the array.

var input = ["A", "A", "B", "B", "C"],
    lowest = _(input)
        .countBy()                    // { A: 2, B: 2, C: 1 }
        .toPairs()                    // [["A", 2], ["B", 2], ["C", 1]]
        .minBy(1)                     // ["C", 1]
        [0];

console.log(lowest);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

This could be achieved by the following:

var tally = {"A":2, "B":2, "C":1};

var tallyItemSmallestKey = Object
.entries(tally)
.sort((entry0, entry1) => entry0[1] - entry1[1] < 0 ? -1 : 1)
.map(entry => entry[0])[0];

console.log( tallyItemSmallestKey );

That could be shortened to a non _ solution if you sort the input array:

const input = ["A","A","B","B","C"].sort();

let curr = { count: 0 }, result = { count: 0 };
for(const value of input) {
  if(value === curr.value) curr.count++;
  else curr = { value, count: 1 };

  if(curr.count > result.count) result = curr;
}

console.log(result.value);

You can get the tallies, then check for the min

const input = ["A", "A", "B", "B", "C"]

// get counts
const tally = input.reduce((obj, char) => {
  if(!obj[char])
   obj[char]=1
  else
   obj[char]++
  return obj
}, {})

// get min
let char = Reflect.ownKeys(tally).reduce((keep,current)=>{
  if(!keep) keep = current
  if(tally[current] < tally[keep])
     keep = current
  return keep
}, '')

console.log(char)
console.log(`'${char}' == 'A'`,char == 'A')
console.log(`'${char}' == 'C'`,char == 'C')

With lodash you could use countBy, entries and minBy for something like this:

const input = ["A", "A", "B", "B", "C"]

const result = _.minBy(_.entries(_.countBy(input)), 1)
console.log(_.head(result))  // or just result[0]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

Or via lodash chaining:

const input = ["A", "A", "B", "B", "C"]

const result = _(input).countBy().entries().minBy(1)

console.log(_.head(result)) // or just result[0]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

You could also do this with ES6 with Array.reduce, Array.sort & Object.entries:

const input = ["A", "A", "B", "B", "C"]

const countBy = arr => arr.reduce((r,c) => (r[c] = (r[c] || 0) + 1, r), {})
const min = arr => arr.sort((a,b) => a[1] - b[1])[0] // sort asc and take first
console.log(min(Object.entries(countBy(input)))[0])

If you don't want to import everything that lodash has to offer, you can't use chaining. This is a _.flow() based version of Nina's answer:

const { flow, countBy, entries, partialRight, minBy, last, head } = _;

const input = ["A", "A", "B", "B", "C"];

const getByLowestCount = flow([
  countBy,
  entries,
  partialRight(minBy, last),
  head
]);

const result = getByLowestCount(input);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

Related