Create ...new Set from Category Array

Hello Support,

I’m trying to return unique values from a query (as opposed to every value) but I’m unable to return the data which is nested in an array.

As you can see on line 47 I’m using ‘…new Set’ to store the unique values from the author field.

https://github.com/albionCircus/logically/blob/main/investigations/index.astro

This is great and works perfectly, but I’d like to be able to create a new Set from the categories field as well (Line 34) so I can display unique category tags.

Screenshot of query attached;

Thanks for your help!

Richard Gill

Hi @albioncircus,

It’s not 100% clear to me what you’re trying to do here, but I THINK you are trying to find unique values in an array of objects, which themselves are in an array of posts? If so, here is an example below. If I’m mistaken, can you please let me know which project this query is associated with so I can make a more realistic example for you?

Otherwise, hopefully this code makes enough sense?

Click to expand sample code
const posts = [
    {
        author: {
            name: "Fake Person"
        },
        categories: [
            {
                tag: "Elections",
                slug: "elections"
            },
            {
                tag: "Bribes",
                slug: "campaign-contributions"
            }
        ]
    },
    {
        author: {
            name: "Notareal Person"
        },
        categories: [
            {
                tag: "Elections",
                slug: "elections"
            },
            {
                tag: "Dirty secrets",
                slug: "dirty-secrets"
            }
        ]
    }
]

// Array.map() will give you a hierarchy
const nestedCategories = posts.map(post => post.categories)
console.log(nestedCategories)

/* returns nested arrays that we don't want
[
  [
    { tag: 'Elections', slug: 'elections' },
    { tag: 'Bribes', slug: 'campaign-contributions' }
  ],
  [
    { tag: 'Elections', slug: 'elections' },
    { tag: 'Dirty secrets', slug: 'dirty-secrets' }
  ]
]
*/

// Array.flatMap() will give you a flattened array
const flatCategories = posts.flatMap(post => post.categories)

console.log(flatCategories)
/* That's better! Now we have a flat array

[
  { tag: 'Elections', slug: 'elections' },
  { tag: 'Bribes', slug: 'campaign-contributions' },
  { tag: 'Elections', slug: 'elections' },
  { tag: 'Dirty secrets', slug: 'dirty-secrets' }
]
*/

// It's easy to get unique values of a single key
const uniqueCategorySlugs = [...new Set(flatCategories.map(category => category.slug))] // Or you can use `category.tag` if you prefer
console.log(uniqueCategorySlugs)

// returns [ 'elections', 'campaign-contributions', 'dirty-secrets' ]

// It's harder if you want to get unique-looking objects (they are compared by reference, not value.
// So they are considered different even if they have the same values but are different objs in memory
// You can use workarounds, like JSON stringifying or using libraries like Lodash or deep-equal


// First we convert all the objects into strings
const flatCategoriesAsStrings = flatCategories.map(category => JSON.stringify(category))

// Then we use Set to find unique strings
const uniqueCategoryStrings = [...new Set(flatCategoriesAsStrings)]

// Then we parse them back into objects
const uniqueCategoryObjects = uniqueCategoryStrings.map(objString => JSON.parse(objString))
console.log(uniqueCategoryObjects)
/* returns 
[
  { tag: 'Elections', slug: 'elections' },
  { tag: 'Bribes', slug: 'campaign-contributions' },
  { tag: 'Dirty secrets', slug: 'dirty-secrets' }
]
 */

To expand a little bit, you can first flatten categories across multiple posts using Array.flatMap(). Then you can compare them in various ways, still using Set, but either you map through a value (like their tag or slug) or you have to do some sort of deep object compare, which is where it gets tricky.

In the example above I just stringify the objects and back (because Set can compare strings for equality, not objects). An easier-to-read (and implement) alternative would be to use a library like lodash’s {uniq}, which turns it into a one-liner.

There are also other approaches, like using a reducer, but in my experience those are harder to read and reason about than just flatMapping through it. In this case performance probably isn’t a huge deal either way, so I’d go for readability over terseness. Up to you though!

Hope that helps?

Hello Roger,

Your ‘Array.flatMap’ suggestion worked perfectly, so thanks so much!

I’ve attached a couple of screenshots - yes my objective was to build a category filter using only unique categories. But as I was using a query for the posts it was returning multiples (i.e, one for each misinformation post so I was ending up with 3 misinformation buttons!).

Categories Before
Categories After

All good now though! Thanks again,

Richard Gill

1 Like

Awesome! Glad it worked.