简体   繁体   中英

How do I count the number of unique elements in an array of objects by an object property?

I have an orders array.

orders = [
  {table_id: 3, food_id: 5},
  {table_id: 4, food_id: 2},
  {table_id: 1, food_id: 6},
  {table_id: 3, food_id: 4},
  {table_id: 4, food_id: 6},
];

I want to write a function to count the number of unique table_id or unique food_id in the array. For eg,

For eg
The list of unique table_id s are 1, 3, 4 which totals 3 .
The list of unique food_id s are 2, 4, 5, 6 which totals 4

How do I perform this?

Something like this might work:

function countTableId(orders){
    // sets are, well, sets of unique items. no duplicates allowed
    let uniqueId = new Set();
    for(let ord of orders){
        uniqueId.add(ord.table_id);
    }
    // the size of the set is the number of unique items we added
    return uniqueId.size;
}

You could do the same with the food_id property in another function.

@Felix King has a one-line way of doing this, so props to him. It uses the set object and Array.prototype.map()

let unique_table_id_count = new Set(orders.map(x => x.table_id)).size
let unique_food_id_count = new Set(orders.map(x => x.food_id)).size

.map() will loop through the array and look at each table_id, then it will try to add that to the set. The set object will discard an element if it already exists, so we only get unique values. Finally, because of chaining, we can just get the size of the set and we're done!

I'm pretty sure you haven't tried yet because it's as simple as it seems :

1 - initialize arrays

var unique_table_ids = [];
var unique_food_ids = [];

2 - loop through orders and populate the arrays

for (var i = 0; i < orders.length; i++) {
    if (unique_table_ids.indexOf(orders[i].table_id) == -1) // if this Table ID wasn't already in the unique table ID list
        unique_table_ids.push(orders[i].table_id);          // Add the table ID to the unique table ID list
    if (unique_food_ids.indexOf(orders[i].table_id) == -1)  // if this Food ID wasn't already in the unique food ID list
        unique_food_ids.push(orders[i].food_id);            // Add the food ID to the unique food ID list
}

Now, with your current order array, the respective values of unique_table_ids and unique_food_ids are :

unique_table_ids // = [3, 4, 1];
unique_food_ids // = [5, 2, 6, 4];

And of course you can get their length :

unique_table_ids.length // = 3
unique_food_ids.length // = 4

Simply:

var results = {};

orders.forEach(function(item) {
  for (var key in item) {
    results[key] = results[key] || [];
    if (results[key].indexOf(item[key]) == -1)
      results[key].push(item[key]);
  }
})

for (var key in results) {
  console.log("The list of unique "+ key +" are "+ results[key] +" which totals "+ results[key].length);
}

few lines, no new data structures except results object, no edits to your existing code and, most important , no pre-knowledge of orders-objects keys needed

Add the values to a Set and get the size.

If you need a es5 solution then you could implement a set class yourself.

You can create a list of the distinct table and food IDs and count them at the same time. This code will create 2 objects that hold the information...

 var orders = [ {table_id: 3, food_id: 5}, {table_id: 4, food_id: 2}, {table_id: 1, food_id: 6}, {table_id: 3, food_id: 4}, {table_id: 4, food_id: 6}, ]; var tables = {}; var foods = {}; for (var i in orders) { var order = orders[i]; var tableId = "table_id_" + order.table_id; var foodId = "food_id_" + order.food_id; if (tables.hasOwnProperty(tableId)) { tables[tableId]++; } else { tables[tableId] = 1; } if (foods.hasOwnProperty(foodId)) { foods[foodId]++; } else { foods[foodId] = 1; } } console.log(tables); console.log(foods); 

To anyone else cringing, I apologise for my use of the variable "foods". It's a habit.

You can combine Array.prototype.sort and Array.prototype.filter :

type Order = {
    table_id: number;
    food_id: number;
}

let orders = [
  {table_id: 3, food_id: 5},
  {table_id: 4, food_id: 2},
  {table_id: 1, food_id: 6},
  {table_id: 3, food_id: 4},
  {table_id: 4, food_id: 6},
] as Order[];

let uniques = orders.sort((a, b) => a.table_id - b.table_id)
    .filter((value, index, array) => index === array.length - 1 || array[index + 1].table_id !== value.table_id);

console.log(uniques.length); // 3

( code in playground )

Did it!

 orders = [ {table_id: 3, food_id: 5}, {table_id: 4, food_id: 2}, {table_id: 1, food_id: 6}, {table_id: 3, food_id: 4}, {table_id: 4, food_id: 6}, ]; var result=[]; var result2=[]; for(var i=0; i<orders.length; i++){ var current=orders[i]; var ti = current.table_id; var push=true; for(var j=0; j<result.length;j++){ if(ti==result[j]){ push = false; } } if(push){result.push(ti);} var fi = current.food_id; push=true; for(var j=0; j<result2.length;j++){ if(fi==result2[j]){ push = false; } } if(push){result2.push(fi);} } console.log(result + " " + result2); console.log("Unique Tables: " + result.length + " Unique Foods: " + result2.length); 

You could use Set and map the wanted parts.

 var orders = [{ table_id: 3, food_id: 5 }, { table_id: 4, food_id: 2 }, { table_id: 1, food_id: 6 }, { table_id: 3, food_id: 4 }, { table_id: 4, food_id: 6 }], getUnique = key => [...new Set(orders.map(o => o[key]))]; console.log(getUnique('table_id')); console.log(getUnique('food_id')); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

 var orders = [ {table_id: 3, food_id: 5}, {table_id: 4, food_id: 2}, {table_id: 1, food_id: 6}, {table_id: 3, food_id: 4}, {table_id: 4, food_id: 6}, {table_id: 4, food_id: 6}, {table_id: 4, food_id: 6} ]; var tableObj = {}, foodObj = {}; orders.forEach(function(e){ tableObj[e.table_id] = null; foodObj[e.food_id] = null; }); var tableUniqueIDs = Object.keys(tableObj); var foodUniqueIDs = Object.keys(foodObj); console.log("table IDs: ", tableUniqueIDs); console.log("food IDs: ", foodUniqueIDs); 

 orders = [ {table_id: 3, food_id: 5}, {table_id: 4, food_id: 2}, {table_id: 1, food_id: 6}, {table_id: 3, food_id: 4}, {table_id: 4, food_id: 6}, ]; function count(arr, key) { res = []; arr.map(obj => obj[key]) .filter(n => res.indexOf(n) <= -1 && res.push(n)) return res.length; } console.log(count(orders, 'table_id')); console.log(count(orders, 'food_id')); 

PS: kind of hackish with the && though...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM