Home > Net >  React Native Search by 2 Dictionary Values
React Native Search by 2 Dictionary Values

Time:03-17

I have some JSON (see end) that contains two values. I need the search to filter based on either value. I documented every part of the search/display of code

Here is my useState variables:

const [dataBackup, setdataBackup] = useState([]) const [dataSource, setdataSource] = useState([])

Here is my FlatList:

 <SectionList
      contentContainerStyle={{ paddingHorizontal: 10}}
      stickySectionHeadersEnabled={false}
      sections={dataSource}
      keyExtractor={(item, index) => {
        return  index.toString();
       }}
      renderSectionHeader={({ section }) => (
        <>
          <Text style={styles.sectionHeader}>{section.title}</Text>
          {section.data ? (
            <OptimizedFlatList
              horizontal
              data={section.data} 
              keyExtractor={(item, index) => {
                return  index.toString();
               }}
              renderItem={({ item }) => <ListItem item={item} navigation={navigation} />}
              showsHorizontalScrollIndicator={false}
            />
          ) : null}
        </>
      )}
      renderItem={({ item, section, navigation }) => {
        if (section.data) {
          return null;
        }
        return <ListItem item={item} navigation={navigation}/>;
      }}
    />

Here is the NOT WORKING filtering logic:

    filterList = (text) => {
  for (let i = 0; i < dataBackup.length; i  ) {
    const t = dataBackup[i];
    newData = t["data"].filter((item) => {
      const itemData = item["name"].toLowerCase();
      const textData = text.toLowerCase();
      setdataSource(itemData.indexOf(textData) > -1)
    });

  }

}

Here is what the data looks like:

[{
    title: 'Leg',
    data:[
        {
      "bodyPart": "lower legs",
      "equipment": "assisted",
      "gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/1708.gif",
      "id": "1708",
      "name": "assisted lying calves stretch",
      "target": "calves",
      "broad_target": "legs",
      "ppl": "legs"
    },
    {
      "bodyPart": "lower legs",
      "equipment": "smith machine",
      "gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/1396.gif",
      "id": "1396",
      "name": "smith toe raise",
      "target": "calves",
      "broad_target": "legs",
      "ppl": "legs"
    }
  ]
},

{
    title: 'Back',
    data:[
        {
      "bodyPart": "Back",
      "equipment": "assisted",
      "gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/1708.gif",
      "id": "1708",
      "name": "Back1",
      "target": "Back",
      "broad_target": "Back",
      "ppl": "Pull"
    },
    {
      "bodyPart": "Back",
      "equipment": "smith machine",
      "gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/1396.gif",
      "id": "1396",
      "name": "Back2",
      "target": "Back",
      "broad_target": "Back",
      "ppl": "Pull"
    }
  ]
}]

I want it to filter by Title OR by name

CodePudding user response:

So i do not see a title field on your data object. But this is how you can do it for finding exact match. The implementation is for filtering on name and equipment. If you need a partial match you can do item.name.toLowerCase().split(/\s |./).includes(query.toLowerCase())

const dataSample = [
    { "title": "red", 
      "data": [{
          "bodyPart": "back",
          "broad_target": "back",
          "equipment": "cable",
          "gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/0007.gif",
          "id": 1,
          "name": "alternate item",
          "ppl": "pull",
          "target": "lats",
          "thumbnail": "https://images.unsplash.com/photo-1640334554717-a7a4ce5d0045?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=296&q=20"
        }
      ]
    },
    { "title": "blue", 
      "data": [{
          "bodyPart": "back",
          "broad_target": "back",
          "equipment": "cable",
          "gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/0007.gif",
          "id": 1,
          "name": "alternate",
          "ppl": "pull",
          "target": "lats",
          "thumbnail": "https://images.unsplash.com/photo-1640334554717-a7a4ce5d0045?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=296&q=20"
        }
      ]
    }
]
    
const filterData = (data = [], query) =>
  data.filter(item => item.title.toLowerCase() === query.toLowerCase()
  ||
  item.data.find(element => element.name.toLowerCase() === query.toLowerCase())
  )
console.log(filterData(dataSample, 'alternate'))
console.log(filterData(dataSample, 'red'))

Try this out, updated as per your comment:

const filterData = (data = [], query) =>
data.filter(item => item.title.toLowerCase().includes(query.toLowerCase())
||
item.data.find(element => element.name.toLowerCase().includes(query.toLowerCase()))
)

<SearchBar 
    onChangeText={(text) =>
        text.length !== 0 ? 
        setdataSource(filterData(dataBackup, text)): 
        setdataSource(dataBackup)} 
    onClearPress={() => setdataSource(dataSource)} 
    />

When i gave it another thought, this is the perfect solution for your case covering all other scenarios.

useEffect(() => {
    console.log("search key", searchKey)
    if(searchKey.length != 0){
        setdataSource(filterData(dataBackup, searchKey))
    } else{
        setdataSource(dataBackup)
      } 
  }, [searchKey])

  const filterData = (data = [], query) => {
    
      const foundItems = searchForItem(data, query)
      const foundDataItems = searchForDataInItem(data, query)
      const uniqueItemTitles = [...new Set([...Object.keys(foundItems),...Object.keys(foundDataItems)])];
      return uniqueItemTitles.reduce((title, accumulator) => {
          const itemHadTitleMatchedToQuery = foundItems.find(element => element.title === title)
          const itemHadDataMatchedToQuery = foundDataItems.find(element => element.title === title)
          if(itemHadTitleMatchedToQuery) {
                accumulator.push({
                  ...itemHadTitleMatchedToQuery
                })
          } else {
                accumulator.push({
                  ...itemHadDataMatchedToQuery
                })
          }
          return accumulator;
      }, []);

  }

const searchForItem = (data = [], query) => {
    const result = data.filter(item => 
      item.title.toLowerCase().includes(query.toLowerCase())
      
  )

  return result
}

const searchForDataInItem = (data = [], query) => {
  return data.map(item => {
    return { ...item, data: item.data.filter(element => element.name.toLowerCase().includes(query.toLowerCase()))}
  }).filter(item => item.data.length != 0)
}

CodePudding user response:

After working with @Dheeraj Sharma, we came up with:

useEffect(() => {
    console.log("search key", searchKey)
    if(searchKey.length != 0){
        setdataSource(filterData(dataBackup, searchKey))
    } else{
        setdataSource(dataBackup)
      } 
  }, [searchKey])

  const filterData = (data = [], query) => {
    
      const foundItems = searchForItem(data, query)
      const foundData = searchForDataInItem(data, query)

      if(foundItems.length !== 0){
        return foundItems;
      }

      return foundData

  }

const searchForItem = (data = [], query) => {
    const result = data.filter(item => 
      item.title.toLowerCase().includes(query.toLowerCase())
      
  )

  return result
}

const searchForDataInItem = (data = [], query) => {
  return data.map(item => {
    return { ...item, data: item.data.filter(element => element.name.toLowerCase().includes(query.toLowerCase()))}
  }).filter(item => item.data.length != 0)
}
  • Related