遍历嵌套的js对象以查看其关键字是否包含搜索词/短语

时间:2018-08-17 15:49:05

标签: javascript arrays vuejs2

我花了很多时间试图弄清楚如何编写一个JavaScript函数来遍历嵌套的JSON对象,以查看用户提供的单词和/或短语是否包含在其几个键中。

对象的设置如下:

{
  category_name: [{ 
    service: 'This is the name of the service.', 
    link: 'link to the service', 
    keywords: ['one', 'two', 'three']
  }, { 
    service: 'This is the name of another service.', 
    link: 'link to the other service', 
    keywords: ['one', 'four', 'six']
  }],
  category_name2: [{ 
    service: 'This is the name of the service.', 
    link: 'link to the service', 
    keywords: ['one', 'two', 'three']
  }, { 
    service: 'This is the name of another service.', 
    link: 'link to the other service', 
    keywords: ['one', 'four', 'six']
  }]
}

我想要找回的东西是这样的:

如果有人搜索单词“ four”,那么JavaScript函数将搜索所有“ service”和“ keywords”数据字段,以查看是否存在匹配项,并且在此示例中,它将返回所有出现搜索词:

{
  category_name: [{
     service: "This is the name of another service.",
     link: "link to the other service",
     keywords: ["one", "four", "six"]
  }],
  category_name2: [{
    service: "This is the name of another service.",
    link: "link to the other service",
    keywords: ["one", "four", "six"]
  }],
}

感谢您提供的任何帮助。

3 个答案:

答案 0 :(得分:1)

您可以通过在数组上使用.filter()方法来做到这一点。您可以针对某个条件(在关键字数组中找到该术语)进行测试,如果返回true,则包括该项目,否则,则排除该项目-像这样:

var data = {
  "category_name": [{
      "service": "This is the name of the service.",
      "link": "link to the service",
      "keywords": ["one", "two", "three"]
    }, {
      "service": "This is the name of another service.",
      "link": "link to the other service",
      "keywords": ["one", "four", "six"]
    }],
  "category_name2": [{
    "service": "This is the name of the service.",
    "link": "link to the service",
    "keywords": ["one", "two", "three"]
  }, {
    "service": "This is the name of another service.",
    "link": "link to the other service",
    "keywords": ["one", "four", "six"]
  }]
}

function searchProp(data, prop, searchValue) {
  return data[prop].filter(category => category.keywords.includes(searchValue) || category.service.indexOf(searchValue) > -1);
}

function searchData(value) {
  return Object.keys(data).filter(key => key.indexOf("category_name") > -1).reduce((res, currKey) => {
    var currVal = searchProp(data, currKey, value);
    if (currVal.length) {
      res[currKey] = currVal;
    }
    return res;
  }, {})
}

console.log(searchData("four"));
console.log(searchData("one"));
console.log(searchData("another"));

答案 1 :(得分:1)

我假设用户的输入应同时检查servicekeywords。因此,过滤器和查找将完成这项工作。

      function search(data, value) {
            let keys = Object.keys(data);
            let result = keys.map((key)=>{
              let result = data[key].filter((obj) => {
              let has_service = obj.service.match(value);
              if (has_service) return obj;
              // console.log(has_service)
              let has_keyword = obj.keywords.find((key) => {
                let result = key.match(value)
                return result;
              });
              if (has_keyword) return obj;
            });
             if(result.length)
                return { [key] : result };
             return [];
            }).reduce((A, B)=>{ return A.concat(B) }, []);
           return result;
          }

      var data = {
              "category_name": [
                { "service": "This is the name of the service.", "link": "link to the service", "keywords": ["one", "two", "three"]},
              { "service": "This is the name of another service.", "link": "link to the other service", "keywords": ["one", "four", "six"]}
              ], 
             "category_name2": [
               { "service": "This is the name of the service.", "link": "link to the service", "keywords": ["one", "two", "three"]},
              { "service": "Yet another service.", "link": "link to the other service", "keywords": ["seven", "four", "six"]}
             ]
          };

        console.log(search(data, "four"))

答案 2 :(得分:1)

我将其分为几部分。第一个mapObj非常可重用。这应该证明它的作用。

const square = n => n * n
mapObj(square, {a: 1, b: 2, c: 3})  //=> {a: 1, b: 4, c: 9}

要使用此功能,我们可以使用谓词,通过过滤器调用来映射对象:

const matchesTerm = (term) => {
  const rx = new RegExp(`\\b${term}\\b`, 'i') 
  return (svc) => svc.keywords.includes(term) || rx.test(svc.service)
}

然后可以在main函数中将其用作传递给filter的谓词,如下所示:

const findTerm = (data) => (term) =>
  mapObj((category) => category.filter(matchesTerm(term)), data)

我发现像这样解决问题可以帮助我找到常见的模式(mapObj),并使设计牢记在心。

这假定您只希望关键字完全匹配,而描述中只希望部分匹配。但是,如果不是这种情况,则很容易更改谓词。关键字也区分大小写,但描述不区分大小写。同样,这可以随时更改。


您可以看到它的作用:

const mapObj = (fn, obj) => Object.keys(obj).reduce(
  (acc, key) => Object.assign(acc, {[key]: fn(obj[key])}),
  {}
)

const matchesTerm = (term) => {
  const rx = new RegExp(`${term}`, 'i') 
  return (svc) => svc.keywords.includes(term) || rx.test(svc.service)
}

const findTerm = (data) => (term) =>
  mapObj((category) => category.filter(matchesTerm(term)), data)

const data = {
  category_name1: [
    { 
      service: 'This is the name of the service.', 
      link: 'link to the service', 
      keywords: ['one', 'two', 'three']
    },
    { 
      service: 'This is the name of another service.', 
      link: 'link to the other service', 
      keywords: ['one', 'four', 'six']
    }
  ],
  category_name2: [
    { 
      service: 'This is the name of the service.', 
      link: 'link to the service', 
      keywords: ['one', 'two', 'three']
    },
    { 
      service: 'This is the name of another service.', 
      link: 'link to the other service', 
      keywords: ['one', 'four', 'six']
    }
  ]

}

console.log(findTerm(data)('four'))    // one match
console.log(findTerm(data)('one'))     // several matches
console.log(findTerm(data)('anoth'))   // partial match
console.log(findTerm(data)('missing')) // no match


更新

评论要求提供没有箭头功能的版本。这一个用函数表达式替换了它们,并用includes替换了indexOf(...) > -1。它不提供a shim for Object.assign。您将必须单独进行操作。

const mapObj = function(fn, obj) {
  return Object.keys(obj).reduce(
    function(acc, key) {
      const newObj = {};
      newObj[key] = fn(obj[key]);
      return Object.assign(acc, newObj)
    }, 
    {}
  )
}

const matchesTerm = function(term) {
  const rx = new RegExp(`${term}`, 'i') 
  return function(svc) {
    return svc.keywords.indexOf(term) > -1 || rx.test(svc.service)
  }
}

const findTerm = function(data) {
  return function(term) {
    return mapObj(function(category) {
      return category.filter(matchesTerm(term))
    }, data)
  }
}


const data = {"category_name1": [{"keywords": ["one", "two", "three"], "link": "link to the service", "service": "This is the name of the service."}, {"keywords": ["one", "four", "six"], "link": "link to the other service", "service": "This is the name of another service."}], "category_name2": [{"keywords": ["one", "two", "three"], "link": "link to the service", "service": "This is the name of the service."}, {"keywords": ["one", "four", "six"], "link": "link to the other service", "service": "This is the name of another service."}]}

console.log(findTerm(data)('four'))    // one match
console.log(findTerm(data)('one'))     // several matches
console.log(findTerm(data)('anoth'))   // partial match
console.log(findTerm(data)('missing')) // no match