1

I have a JSON object like this:

long_array =    
[
{"location":"Kirrawee","identity_long":"student"},
{"location":"Kirrawee","identity_long":"visitor"},
{"location":"Kirrawee","identity_long":"visitor"},
{"location":"Kirrawee","identity_long":"worker"},
{"location":"Sutherland","identity_long":"student"},
{"location":"Sutherland","identity_long":"resident"},
{"location":"Sutherland","identity_long":"worker"},
{"location":"Sutherland","identity_long":"resident"},
{"location":"Miranda","identity_long":"resident"},
{"location":"Miranda","identity_long":"worker"},
{"location":"Miranda","identity_long":"student"},
{"location":"Miranda","identity_long":""},
{"location":"Miranda","identity_long":"worker"},
{"location":"Miranda","identity_long":"resident"}
];

What I want to achieve is an object like the following:

grouped_and_counted_location_and_identity =
[
{"location":"Kirrawee","identity":"student","count":1},
{"location":"Kirrawee","identity":"visitor","count":2},
{"location":"Kirrawee","identity":"worker","count":1},
{"location":"Sutherland","identity":"student","count":1},
{"location":"Sutherland","identity":"resident","count":2},
{"location":"Sutherland","identity":"worker","count":1},
{"location":"Miranda","identity":"resident","count":2},
{"location":"Miranda","identity":"worker","count":2},
{"location":"Miranda","identity":"student","count":1}
];

I find this extremely easy to achieve in the R language, where I would do this like:

long_array %>%
    group_by(location, identity_long) %>%
    summarise(n = n())

Or even just

long_array %>%
    count(location, identity_long)

But how can I achieve this in javascript?

I just want to group the JSON object by two properties and count the number of identical occurrences.

Davide Lorino
  • 875
  • 1
  • 9
  • 27

7 Answers7

3

create a set of unique values of array and compair it with all values present in long_array and count the same values and save it in new array

const long_array = [{
    "location": "Kirrawee",
    "identity_long": "student"
  },
  {
    "location": "Kirrawee",
    "identity_long": "visitor"
  },
  {
    "location": "Kirrawee",
    "identity_long": "visitor"
  },
  {
    "location": "Kirrawee",
    "identity_long": "worker"
  },
  {
    "location": "Sutherland",
    "identity_long": "student"
  },
  {
    "location": "Sutherland",
    "identity_long": "resident"
  },
  {
    "location": "Sutherland",
    "identity_long": "worker"
  },
  {
    "location": "Sutherland",
    "identity_long": "resident"
  },
  {
    "location": "Miranda",
    "identity_long": "resident"
  },
  {
    "location": "Miranda",
    "identity_long": "worker"
  },
  {
    "location": "Miranda",
    "identity_long": "student"
  },
  {
    "location": "Miranda",
    "identity_long": ""
  },
  {
    "location": "Miranda",
    "identity_long": "worker"
  },
  {
    "location": "Miranda",
    "identity_long": "resident"
  }
];
const unique_values = [...new Map(long_array.map(obj => [JSON.stringify(obj), obj])).values()]; // this will remove duplicate values from long_array
const result = unique_values.map((val) => { // iterate in unique_values
  let count = 0;
  long_array.forEach((item) => {
    item.location == val.location && item.identity_long === val.identity_long && count++
  }); //iterate in long_array and count same values
  return { ...val,
    count: count
  }
})
console.log(result);
Lioness100
  • 8,260
  • 6
  • 18
  • 49
1

You can use library such as lodash and use it's group by function to do this easy way, bit time consuming way would be to implement your own group by function.

let long_array = [{
    "location": "Kirrawee",
    "identity_long": "student"
  },
  {
    "location": "Kirrawee",
    "identity_long": "visitor"
  },
  {
    "location": "Kirrawee",
    "identity_long": "visitor"
  },
  {
    "location": "Kirrawee",
    "identity_long": "worker"
  },
  {
    "location": "Sutherland",
    "identity_long": "student"
  },
  {
    "location": "Sutherland",
    "identity_long": "resident"
  },
  {
    "location": "Sutherland",
    "identity_long": "worker"
  },
  {
    "location": "Sutherland",
    "identity_long": "resident"
  },
  {
    "location": "Miranda",
    "identity_long": "resident"
  },
  {
    "location": "Miranda",
    "identity_long": "worker"
  },
  {
    "location": "Miranda",
    "identity_long": "student"
  },
  {
    "location": "Miranda",
    "identity_long": ""
  },
  {
    "location": "Miranda",
    "identity_long": "worker"
  },
  {
    "location": "Miranda",
    "identity_long": "resident"
  }
];

function addItemCounts(items, groupByKeys) {
  var groups = _.groupBy(long_array, obj => {
    return groupByKeys.map(key => obj[key]).join('-');
  });

  return _.map(groups, g => ({
    ...g[0],
    count: g.length
  }));
}

console.log(addItemCounts(long_array, ['identity_long', 'location']));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
Dipen Shah
  • 25,562
  • 1
  • 32
  • 58
1

Here is a solution with time complexity of O(n*logn) and space complexity of O(n)

let long_array =
  [
    { "location": "Kirrawee", "identity_long": "student" },
    { "location": "Kirrawee", "identity_long": "visitor" },
    { "location": "Kirrawee", "identity_long": "visitor" },
    { "location": "Kirrawee", "identity_long": "worker" },
    { "location": "Sutherland", "identity_long": "student" },
    { "location": "Sutherland", "identity_long": "resident" },
    { "location": "Sutherland", "identity_long": "worker" },
    { "location": "Sutherland", "identity_long": "resident" },
    { "location": "Miranda", "identity_long": "resident" },
    { "location": "Miranda", "identity_long": "worker" },
    { "location": "Miranda", "identity_long": "student" },
    { "location": "Miranda", "identity_long": "" },
    { "location": "Miranda", "identity_long": "worker" },
    { "location": "Miranda", "identity_long": "resident" }
  ];

// create map
let map = new Map()

for (let i = 0; i < long_array.length; i++) {
  const s = JSON.stringify(long_array[i])

  if (!map.has(s)) {
    // if the map does not contain the object already
    // i.e. its first occurrence
    map.set(s, {
      location: long_array[i].location,
      identity: long_array[i].identity_long,
      count: 1,
    })
  } else {
    // if it no first occurrence
    // increase the count straight way
    map.get(s).count++
  }
}

const result = Array.from(map.values())
console.log(result)
Shivam Singla
  • 2,117
  • 1
  • 10
  • 22
1

This is what i've managed to come up with, now that i saw the other answers it's very complicated, but i think it works:

const grouped_and_counted_location_and_identity = [];

const sort = () => {
  long_array.forEach((el) => {
    let count = 0;
    for (let i = long_array.indexOf(el); i < long_array.length; i++) {
      if (
        el.location == long_array[i].location &&
        el.identity_long == long_array[i].identity_long
      ) {
        count++;
      }
    }
    el.count = count;
    if (
      !grouped_and_counted_location_and_identity.some(
        (elem) =>
          elem.location == el.location && elem.identity_long == el.identity_long
      )
    ) {
      grouped_and_counted_location_and_identity.push(el);
    }
  });
};

sort();

console.log(grouped_and_counted_location_and_identity);

Stefan Dobre
  • 138
  • 6
0

One way to approach this would be to convert the objects to strings (using something like JSON.stringify). Since comparing objects in JavaScript compares the instance, and not the fields/values, converting to strings would make it easier to identify duplicates. You could then do something like this to get a count of the duplicates! Hopefully this helps!

zeepk
  • 108
  • 6
0
  1. Create a mapping of counts based on a string containing the location and the identity_long:

    { Kirrawee-student: 1, ... }
    
  2. Map through each entry and transform it to the desired structure.

const long_array = [
  { "location": "Kirrawee", "identity_long": "student" },
  { "location": "Kirrawee", "identity_long": "visitor" },
  { "location": "Kirrawee", "identity_long": "visitor" },
  { "location": "Kirrawee", "identity_long": "worker" },
  { "location": "Sutherland", "identity_long": "student" },
  { "location": "Sutherland", "identity_long": "resident" },
  { "location": "Sutherland", "identity_long": "worker" },
  { "location": "Sutherland", "identity_long": "resident" },
  { "location": "Miranda", "identity_long": "resident" },
  { "location": "Miranda", "identity_long": "worker" },
  { "location": "Miranda", "identity_long": "student" },
  { "location": "Miranda", "identity_long": "" },
  { "location": "Miranda", "identity_long": "worker" },
  { "location": "Miranda", "identity_long": "resident" }
];

const countsMap = _.countBy(long_array, e => `${e.location}-${e.identity_long}`);
const longArrayWithCount = Object.entries(countsMap).map(([group, count]) => {
 const [location, identity_long] = group.split('-');

 return { location, identity_long, count };
});

console.log(longArrayWithCount);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"></script>
JBallin
  • 8,481
  • 4
  • 46
  • 51
0

This can be achieved using Array.reduce and Object.values along with Optional Chaining

let long_array = [{location:'Kirrawee',identity_long:'student'},{location:'Kirrawee',identity_long:'visitor'},{location:'Kirrawee',identity_long:'visitor'},{location:'Kirrawee',identity_long:'worker'},{location:'Sutherland',identity_long:'student'},{location:'Sutherland',identity_long:'resident'},{location:'Sutherland',identity_long:'worker'},{location:'Sutherland',identity_long:'resident'},{location:'Miranda',identity_long:'resident'},{location:'Miranda',identity_long:'worker'},{location:'Miranda',identity_long:'student'},{location:'Miranda',identity_long:''},{location:'Miranda',identity_long:'worker'},{location:'Miranda',identity_long:'resident'}];

const formatData = (data) => {
  const finalRes = data.reduce((res, {location, identity_long}) => {
  //Formatting a composite key using `location` and `identity_long`
    let key = `${location}_${identity_long}`;
    res[key] = {
      ...res[key],
      location,
      identity: identity_long,
      //Accessing the count form the result/accumulator if exists and if not assigning `0` and updating by 1
      count: (res[key]?.count || 0) + 1
    }
    return res;
  }, {});
  //Finally returning the values of the object 
  return Object.values(finalRes)
}

console.log(formatData(long_array))
.as-console-wrapper {
  max-height: 100% !important;
}
Nithish
  • 5,393
  • 2
  • 9
  • 24