0

Using TypeScript I have an array of objects which may contain the same values as other objects in the array. The following array, for example, contains objects that have the value "intent". I would like the find the top 3 most commonly occurring intents:

[
  {
    "intent": "hello",
    "other_value": "blah"
  },
  {
    "intent": "hello",
    "other_value": "blahblah"
  },
  {
    "intent": "hi",
    "other_value": "anothervalue"
  },
  {
    "intent": "hello",
    "other_value": "what?"
  },
  {
    "intent": "hello",
    "other_value": "eh?"
  },
  {
    "intent": "hi",
    "other_value": "okthen"
  },
  {
    "intent": "yo",
    "other_value": "alright"
  },
  {
    "intent": "hi",
    "other_value": "yes"
  },
  {
    "intent": "yo",
    "other_value":"yawhat?"
  },
  {
    "intent": "hey",
    "other_value": "meh"
  }
]

I'm trying to get some sort of result that will easily show me the top 3, maybe a key/value pair array or something:

[
  {
    "intent": "hello",
    "occurrences": 4
  },
  {
    "intent": "hi",
    "occurrences": 3
  },
  {
    "intent": "yo",
    "occurrences": 2
  }
]

Below is my attempt at a solution:

function top3(array) {
    let results = [];
    array.forEach(item => {
        if (results[item.intent] != null) {
          results[item.intent] += 1
        } else {
          results[item.intent] = 1;
        }
    });

    results = results.sort();
    return results.slice(0, 3);
}

However this only returns an array of the occurrence values and not the name of the intent themselves. So I am struggling to find with the array which value belongs to which intent.

I tried following the answers posted in this solution:

Get the element with the highest occurrence in an array

However, I couldn't figure out how to find n occurrences, instead just the top occurrence. I wasn't sure how to use that logic to carry on to find the next few occurrences.

blueprintchris
  • 1,053
  • 1
  • 16
  • 38
  • 1
    Are you look for any answer or a "best" answer? Easiest would be to use the algorithm you found for returning most occurring element, delete the most occurring element from the list, then re-run the algorithm. This question would be more helpful if you first attempted a solution and provided code- then we could guide you from there. – chevybow Apr 26 '18 at 14:36
  • 1
    Create a counter for intents and sort it desc, there should be a lot of similar questions. Also, you don't show any attempt. – ASDFGerte Apr 26 '18 at 14:38
  • I have edited my answer to include my attempt at a solution. Please consider your removing your down-votes if my question is now of satisfactory standard. Thanks. @AdamS – blueprintchris Apr 27 '18 at 09:24

2 Answers2

3

Use Array#reduce to create such groups:

const source = [{"intent":"hello","other_value":"blah"},{"intent":"hello","other_value":"blahblah"},{"intent":"hi","other_value":"anothervalue"},{"intent":"hello","other_value":"what?"},{"intent":"hello","other_value":"eh?"},{"intent":"hi","other_value":"okthen"},{"intent":"yo","other_value":"alright"},{"intent":"hi","other_value":"yes"},{"intent":"yo","other_value":"yawhat?"},{"intent":"hey","other_value":"meh"}];

const g = source.reduce((acc, item) => {
  if(!acc[item.intent]) {
    acc[item.intent] = 0;
  }
  
  acc[item.intent]++;
  return acc;
}, {});

let top3 = Object.entries(g).sort((a, b) => b[1] - a[1]).slice(0, 3);

console.log('groups', g);
console.log('top 3', top3);

You may further convert the top 3 collection to corresponding objects using Array#map, like this:

top3.map(item => { [item[0]]: item[1] });
31piy
  • 23,323
  • 6
  • 47
  • 67
3

You could get an object with the count first by building an array of objects, sort it descending by occurences and slice the array for getting top three elements only.

var data = [{ intent: "hello", other_value: "blah" }, { intent: "hello", other_value: "blahblah" }, { intent: "hi", other_value: "anothervalue" }, { intent: "hello", other_value: "what?" }, { intent: "hello", other_value: "eh?" }, { intent: "hi", other_value: "okthen" }, { intent: "yo", other_value: "alright" }, { intent: "hi", other_value: "yes" }, { intent: "yo", other_value: "yawhat?" }, { intent: "hey", other_value: "meh" }],
    count = data
        .reduce((r, { intent }) => {
            r[intent] = r[intent] || { intent, occurences: 0 };
            r[intent].occurences++;
            return r;
        }, {}),
    top3 = Object
        .values(count)
        .sort((a, b) => b.occurences - a.occurences)
        .slice(0, 3);
    
console.log(top3);
console.log(count);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Despite all the downvotes I received for this, people still managed to post decent answers. Thanks for your efforts. This worked for me. – blueprintchris Apr 27 '18 at 09:06