Home > Software engineering >  Javascript search filter to a array?
Javascript search filter to a array?

Time:11-24

I am building a nextjs application. Everything is okay. But in one component I am facing a problem. Actually I can't understand the functionality. I have an Array with Sub-Array. I have to add search filter to that Array. And the function will return when match an item or sub item. But I can't understand..

Here is the code.

This id data-

export default [
    {
        name: "Top Wear",
        sub: [
            { name: "T-Shirts" },
            { name: "Casual Shirts" }
        ]
    }
    {
        name: "Western Wear",
        sub: [
            { name: "Dresses" },
            { name: "Jumpsuits" }
        ]
    },
    { name: "Plus Size" },
    { name: "Sunglasses and Frames" },
    {
        name: "Footwear",
        sub: [
            { name: "Casual Shoes" },
            { name: "Sports Shoes" }
        ]
    },
    {
        name: "Lingerie & Sleepwear",
        sub: [
            { name: "Bra" },
            { name: "Briefs" }
        ]
    },
    {
        name: "Sports & Active Wear",
        sub: [
            { name: "Sports Shoes" },
            { name: "Sports Sandals" },
            { name: "Active T-Shirts" },
            { name: "Active T-Shirts" },
        ]
    },
    {
        name: "Gadgets",
        sub: [
            { name: "Smart Wearables" },
            { name: "Fitness Gadgets" }
        ]
    },
    { name: "Bag & Backpacks" },
    { name: "Luggages & Trolleys " }
]

Here I want to add search function that match direct name or sub.name. I can't understand How can I write function. I can do that if there are just one Array no Sub-Array..

This code is for first Array not Sub-Array

import { useState, useEffect } from "react";

//Data
import CategoriesData from "Data/Header/SearchBar.data";

const Categories = ({ setFilterData, filterData }) => {
    const [categories, setCategories] = useState(CategoriesData);
    const [input, setInput] = useState("");
    useEffect(() => {
        if (input.length > 0) {
            const mathches = CategoriesData.filter(item => {
                const escapeRegExp = (str) => str.replace(/[\-\[\]\/\{\}\(\)\*\ \?\.\\\^\$\|]/g, "\\$&")
                const regex = new RegExp(escapeRegExp(input), "gi");
                return item.name.match(regex);
            })
            setCategories(mathches);
        } else {
            setCategories(CategoriesData);
        }
    }, [CategoriesData, input]);
    return (
        <Box>
            <Box>
                <InputBase
                    value={input}
                    onChange={(e) => setInput(e.target.value)}
                />
            </Box>
            <List>
                {categories &&
                    categories.map((category, i) => (
                        <Box key={i}>
                            <ListItem>
                                <ButtonBase>
                                    <Typography>
                                        {category.name}
                                    </Typography>
                                </ButtonBase>
                            </ListItem>
                            {category.sub &&
                                category.sub.map((subCategory, i) => (
                                    <ListItem key={i}>
                                        <ButtonBase>
                                            <Typography>
                                                {subCategory.name}
                                            </Typography>
                                        </ButtonBase>
                                    </ListItem>
                                ))
                            }
                        </Box>
                    ))
                }
            </List>
        </Box>
    );
};

export default Categories;

Please help me..

CodePudding user response:

Here's a recursive solution -- search() keeps an array of matches within the data, and whenever it encounters a child array named sub it calls itself again with just that subarray.

Not implemented: case-sensitivity, error handling, or useful output beyond a bare array of matched strings.

const search = (str, data) => {
  let out = [];
  for (let obj of data) {
    if (obj.name.indexOf(str) > -1) {
      out.push(obj.name)
    }
    
    if (obj.sub) { 
      // recurse if there's a child array
      out.push(search(str, obj.sub))
    }
  }
  return out.flat()
}

output = search("e", [{
    name: "Top Wear",
    sub: [{
        name: "T-Shirts"
      },
      {
        name: "Casual Shirts"
      }
    ]
  }, {
    name: "Western Wear",
    sub: [{
        name: "Dresses"
      },
      {
        name: "Jumpsuits"
      }
    ]
  },
  {
    name: "Plus Size"
  },
  {
    name: "Sunglasses and Frames"
  },
  {
    name: "Footwear",
    sub: [{
        name: "Casual Shoes"
      },
      {
        name: "Sports Shoes"
      }
    ]
  },
  {
    name: "Lingerie & Sleepwear",
    sub: [{
        name: "Bra"
      },
      {
        name: "Briefs"
      }
    ]
  },
  {
    name: "Sports & Active Wear",
    sub: [{
        name: "Sports Shoes"
      },
      {
        name: "Sports Sandals"
      },
      {
        name: "Active T-Shirts"
      },
      {
        name: "Active T-Shirts"
      },
    ]
  },
  {
    name: "Gadgets",
    sub: [{
        name: "Smart Wearables"
      },
      {
        name: "Fitness Gadgets"
      }
    ]
  },
  {
    name: "Bag & Backpacks"
  },
  {
    name: "Luggages & Trolleys "
  }
])

console.log(output)
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

Of course you can do recursive search, but you can also flatten the search data so it has no depth and then do the exact same search you are doing in your code. In order to flatten your data you can use array functions:

data.flatMap(v => v.sub).concat(data.map(({sub, ...item}) => item))

Keep in mind this does not alter the main data array.

So first of all it uses flatMap to get the sub properties and flattens them, so at this point you have an array of sub categories, then it concatenates it with the list of main categories (only using map their sub property is now removed cause its flattened). In the end you have an array of 28 categories (10 main 18 sub) to filter. You can chain the filter method and keep going.

CodePudding user response:

Instead of filter use reduce. You can filter and modify the sub-array same time. Using reduce help to manipulate data and filter same time.

You can modify logic as your need.

Sample Code:

const mathches = CategoriesData.reduce((acc, item) => {
  const escapeRegExp = (str) => str.replace(/[\-\[\]\/\{\}\(\)\*\ \?\.\\\^\$\|]/g, "\\$&")
  const regex = new RegExp(escapeRegExp(input), "gi");
  if(item.sub) {
    const sub = item.sub.filter(({name}) => name.match(regex))
    if(sub.length){
      acc.push({name: item.name, sub})
    }
  }
  else if(item.name.match(regex)){
    acc.push(item)
  }
  return acc;
},[])

Example:

const data = [
  {
    name: "Top Wear",
    sub: [{ name: "T-Shirts" }, { name: "Casual Shirts" }],
  },
  {
    name: "Western Wear",
    sub: [{ name: "Dresses" }, { name: "Jumpsuits" }],
  },
  { name: "Plus Size" },
  { name: "Sunglasses and Frames" },
  {
    name: "Footwear",
    sub: [{ name: "Casual Shoes" }, { name: "Sports Shoes" }],
  },
  {
    name: "Lingerie & Sleepwear",
    sub: [{ name: "Bra" }, { name: "Briefs" }],
  },
  {
    name: "Sports & Active Wear",
    sub: [
      { name: "Sports Shoes" },
      { name: "Sports Sandals" },
      { name: "Active T-Shirts" },
      { name: "Active T-Shirts" },
    ],
  },
  {
    name: "Gadgets",
    sub: [{ name: "Smart Wearables" }, { name: "Fitness Gadgets" }],
  },
  { name: "Bag & Backpacks" },
  { name: "Luggages & Trolleys " },
];

const search = (data, input) => {
  const mathches = data.reduce((acc, item) => {
    const escapeRegExp = (str) =>
      str.replace(/[\-\[\]\/\{\}\(\)\*\ \?\.\\\^\$\|]/g, "\\$&");
    const regex = new RegExp(escapeRegExp(input), "gi");
    
    if (item.sub) {
      const sub = item.sub.filter(({ name }) => name.match(regex));
      if (sub.length) {
        acc.push({ name: item.name, sub });
      }
    } else if (item.name.match(regex)) {
      acc.push(item);
    }

    return acc;
  }, []);
  return mathches;
};

console.log(search(data, "Top Wear"));
console.log(search(data, "Sandals"));
console.log(search(data, "Shoes"));
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related