Help with accessing/filtering a nested value in react state array

Hi All, I wondered if anyone could help -

I am practising React/Gatsby by building an product search filter, I have tried map, set, filter, forEach and various other methods from articles for days (The main ones and the problems are below) but I just can’t seem to access the right value in the state to filter it or can’t filter the mapped itemtype I need to remove the duplicates and return the unique values to set to the selectOptions state. My code and some of the options that I have tried are explained below it would be really great for any pointers as to where I am going wrong when trying to access the values that I need. I am trying to filter the .itemtype in the state object for duplicates and return only the unique values. Thanks

{/* A sample of the state ItemType array looks as follows:

itemType:
  Array[5]
    0:
     {…}
      node:
       {…}
        id:"DatoCmsProduct-709568-en"
        itemtype: "T-shirt"
*/}

import React, {Component} from 'react'
import { graphql, StaticQuery } from 'gatsby'
import ShopDataItem from './ShopDataItem'

class ShopData extends Component {
  constructor(props) {
    super(props)
    this.state = {
      itemType: [],
      selectOptions: []
    }
    this.getSelect = this.getSelect.bind(this)
  }

componentDidMount() {
  let items = this.props.itemData.edges

  items.forEach(item => {
    this.setState(previous => ({
        itemType: [...previous.itemType, item]
    }))
})
}
Have tried the following to filter out duplicates ============
{/*
  ----- The idea was to pass item.node.itemtype to getSelect(), set to state to then filter the state array for duplicates and update state again with filtered array but returns selection.forEach is not a function
  getSelect(SelectItem) {
    console.log(SelectItem)
    let selection = SelectItem
    selection.forEach(item => {
      this.setState(previous => ({
          selectOptions: [...previous.selectOptions, item]
      })) }) }
*/}

{/*
This one returns an array of letters of each individual item name with duplicates removed not an array of all items when I pass item.node.itemtype to getSelect()
--------------------------------------------------
getSelect(item) {
  console.log(item)
  const uniqueNames = Array.from(new Set(item));
  console.log(uniqueNames)
}
*/}
{/*

This Returns a new array for each individual item not an array of all items
--------------------------------
getSelect(item) {
  console.log(item)
  const uniqueNames = Array.from(new Set([item]));
  console.log(uniqueNames)
}

{/*

This returns an array for each individual item, not an array of all items.
-----------------------------------
getSelect(item) {
  console.log(item)
  let names = []
  let uniqueNames = names.concat(item);
  console.log(uniqueNames)
}
*/}

{/*} This returns an filtered array of node objects when I use filter in the render function. 
CONSOLE OUTPUT:
(5) [{…}, {…}, {…}, {…}, {…}]
0:
node:
id: "DatoCmsProduct-709568-en"
itemtype: "T-shirt"
__proto__: Object
__proto__: Object
1: {node: {…}}
2:
node: {id: "DatoCmsProduct-709571-en", itemtype: "T-Shirt"}
__proto__: Object
3: {node: {…}}
4: {node: {…}}
length: 5
__proto__: Array(0)
------------------------------------------------------
render () {
  const uniqueNames = this.state.itemType.filter((val, id, array) => array.indexOf(val) == id)
  console.log(uniqueNames)
  return (
    <div>
    {uniqueNames.map((item) => {
      return (
        <div key={item.node.id}>
          <ShopDataItem items={item.node.itemtype} />
        </div>
      )
    })}
    </div>
  )
}
}

*/}
{/* Nothing happens at all with this one when I pass the state.itemType array
getSelect(arr) {
  const map = new Map();
  arr.forEach(v => map.set(v.node.id, v))
  return [...map.values()];
}
*/}



render () {
  return (
    <div>
    {this.state.itemType.map((item) => {
      return (
        <div key={item.node.id}>
          {/* {this.getSelect(item.node.itemtype)} */}
          <ShopDataItem items={item.node.itemtype} />
        </div>
      )
    })}
    </div>
  )
}
}

export default () => (
  <StaticQuery
    query={graphql`
      query Methods {
        allDatoCmsProduct {
          edges {
            node {
              id
              itemtype
            }
          }
        }
      }
    `}
    render={data => <ShopData itemData={data.allDatoCmsProduct} />}
  />
);

Map is not the correct data type. Set is useful here, but you’ll probably have to convert the end result to an array. Whatever way you do it, build the array first, then setState.

The mathod map may work, as a first step to convert the array of objects to an array of itemtype values. Then you take that array and either filter out duplicates, or put it into a Set (then probably convert back to an array).

forEach will work exactly the same as a loop: set up a data structure to put the itemtype values into, and run through the array putting the values in.

reduce would allow you to do the above pretty easily.

Can you post a full example JS object, not the thing that console log prints? I can suggest how to do it, but the structure is not clear from the colsole log snippet.

Thanks so much! here is a sample of the JS object,


itemType: [
  0: {
    node: {
      id: 'id here',
      itemtype: 'T-Shirt'
    }
  }
  1: {
    node: {
      id: 'id here',
      itemtype: 'T-Shirt'
    }
  },
  2: {
    node: {
      id: 'id here',
      itemtype: 'T-Shirt'
    }
  },
  3 : {
    node: {
      id: 'id here',
      itemtype: 'T-Shirt'
    }
  }
]

so:

const justItemTypes = itemType.map(({node: { itemType: itype}}) => itype);
// or
const justItemTypes = itemType.map(item => item.node.itemtype);

Then you’d need to filter it somehow to make it unique: it is just an array of strings so that step is fairly simple.

In one step with reduce:

const justUniqItemTypes = [...itemType.reduce((set, item) => set.add(item.node.itemtype), new Set())];

that’s a bit cryptic though. Bit more verbose, in a function, imperative code:

function uniqItemTypes(itemTypes) {
  const uniqItemTypes = [];
  const usedItemTypes = {};

  for (const item of itemTypes) {
    const iType = item.node.itemtype;
    if (!(iType in usedItemTypes)) {
      usedItemTypes[iType] = null;
      uniqItemTypes.push(iType);
    }
  }

  return uniqItemTypes;
}

Awesome! Thanks so much for your help really appreciate it, makes perfect sense now

1 Like