Home > Blockchain >  Grouping Data in TypeScript Array
Grouping Data in TypeScript Array

Time:12-30

I have JSON data that looks like this:

[
    {
        "id": 1,
        "tags": [
            "Test 1",
            "Test 2",
            "Test 3"
        ]
    },
    {
        "id": 2,
        "tags": [
            "Test 2",
            "Test 3",
            "Test 4"
        ]
    },
    {
        "id": 3,
        "tags": [
            "Test 3",
            "Test 4"
        ]
    }
]

I would like to transform this into data that looks like this:

[
    {
        "name": "Test 1",
        "count": 1
    },
    {
        "name": "Test 2",
        "count": 2
    },
    {
        "name": "Test 3",
        "count": 3
    },
    {
        "name": "Test 4",
        "count": 1
    }
]

I can think of some brute ways to do this, but I'm hoping there is something more performant and a little sexier? Possibly using .groupBy() or .reduce()?

Thanks for taking the time to check out my question.

CodePudding user response:

I would:

interface Item {
  id: number,
  tags: string[]
}

function countOccurences(a: string[]) {
  return a.reduce(function (acc: {[key: string]: number}, curr: string) {
    acc[curr] ??= 0;
    acc[curr]  ;
    return acc;
  }, {});
}

const data: Item[] = JSON.parse(json);
const tagOccurences = countOccurences(data.flatMap(o => o.tags))

Playground link

CodePudding user response:

You can use reduce inside reduce to group the tags.

const array = [{
    id: 1,
    tags: ['Test 1', 'Test 2', 'Test 3'],
  },
  {
    id: 2,
    tags: ['Test 2', 'Test 3', 'Test 4'],
  },
  {
    id: 3,
    tags: ['Test 3', 'Test 4'],
  },
];

const frequencies = Object.values(array.reduce((acc, curr) =>
  curr.tags.reduce(
    (nAcc, tag) => ((nAcc[tag] ??= {name: tag,count: 0}),nAcc[tag].count  ,nAcc),
    acc
  ), {}
));
console.log(frequencies);


In TypeScript:

const array = [{
    id: 1,
    tags: ['Test 1', 'Test 2', 'Test 3'],
  },
  {
    id: 2,
    tags: ['Test 2', 'Test 3', 'Test 4'],
  },
  {
    id: 3,
    tags: ['Test 3', 'Test 4'],
  },
];

type Frequency = {
    name: string,
    count: number
}

const frequencies = Object.values(array.reduce((acc, curr) =>
  curr.tags.reduce(
    (nAcc, tag) => ((nAcc[tag] ??= {name: tag,count: 0}),nAcc[tag].count  ,nAcc),
    acc
  ), {} as Record<string, Frequency>
));
console.log(frequencies); 

Playground

CodePudding user response:

Using for...of iteration and a Map as a cache is a very straightforward approach... and sexy.

TS Playground

type TagsWithId = {
  id: number;
  tags: string[];
};

type TagCount = {
  count: number;
  name: string;
};

function verySexyTagCounter (input: TagsWithId[]): TagCount[] {
  const map = new Map<string, number>();

  for (const {tags} of input) {
    for (const name of tags) {
      map.set(name, (map.get(name) ?? 0)   1);
    }
  }

  return [...map.entries()].map(([name, count]) => ({name, count}));
}

const json = `[{"id":1,"tags":["Test 1","Test 2","Test 3"]},{"id":2,"tags":["Test 2","Test 3","Test 4"]},{"id":3,"tags":["Test 3","Test 4"]}]`;
const input: TagsWithId[] = JSON.parse(json);

const result = verySexyTagCounter(input);
console.log(result);
  • Related