javascript - 如何在包含嵌套对象的对象数组中搜索值

标签 javascript angularjs lodash

我想开发这个搜索/过滤列表的功能。基本上,我将从输入框中获取搜索词,然后我必须从数组中获取包含该搜索词的所有项目。

到目前为止,这是我尝试过的方法,它适用于根级属性,但不适用于嵌套数组/对象:

// Filter List
this.filterList = query => {
  if (typeof query === "string") {
    // transform query to lowercase
    query = query.toLowerCase();
    // clear the current list being displayed
    this.filteredList = [];
    // filter the lsit and store the results with
    // matching criteria in "filteredList" array
    let filteredResults = _.filter(this.itemList, item => {
      if (item && typeof item === "object") {
        // loop over the item object
        for (let property in item) {
          if (item.hasOwnProperty(property)) {
            let key = item[property];
            // address, phone and emails
            if (typeof key === "object" && _.isArray(key)) {
              _.filter(key, element => {
                if (typeof element === "object") {
                  for (let nestedProperty in element) {
                    let nestedKey = element[nestedProperty];
                    if (nestedKey) {
                      nestedKey = nestedKey.toString().toLowerCase();
                    }
                    if (nestedKey && nestedKey.includes(query)) {
                      return item;
                    }
                  }
                }
              });
            } else {
              if (key) key = key.toString().toLowerCase();
              if (key && key.includes(query)) return item;
            }
          }
        }
      }
    });
    // assign the filtered results to the list being displayed
    this.filteredList = [...filteredResults];
  } else {
    // if query is empty or null or anything other than string
    // revert all changes and assign the original list to display list
    this.filteredList = this.itemList;
  }
};

如果有帮助,这是我正在循环的数组中的一个对象:

[
  {
    "id": "number",
    "dealerCode": "string",
    "name": "string",
    "gstin": "string",
    "pan": "string",
    "cin": "string",
    "emails": [
      { 
        "name": "string", 
        "address": "string", 
        "isPrimary": "boolean"
      }
    ],
    "phoneNumbers": [
      { 
        "countryCode": "number", 
        "number": "number", 
        "isPrimary": "boolean"
      }
    ],
    "addresses": [
      {
        "addressLine1": "string",
        "addressLine2": "string",
        "addressLine3": "string",
        "country": "string",
        "state": "string",
        "city": "string",
        "postalCode": "number",
        "isPrimary": "boolean"
      }
    ],
    "status": "string",
    "statusId": "number"
  }
]

我在 AngularJS 和 Lodash 中也是这样做的。

最佳答案

对于像这样的问题,您需要比较基元和对象/数组的异构列表,递归命名函数通常是最好的方法。根据以下假设,这应该可以解决您正在寻找的问题:

  1. 用户的所有条目都被视为字符串。所以99和“99”是一样的。我将在做出此假设的代码中发表评论
  2. 所有条目不区分大小写(全部转换为小写)
  3. 没有设置嵌套对象/数组的深度;下面的解决方案递归地适用于异构列表的任何深度
  4. 如果anything在任何叶节点匹配,整个对象将被返回

以下解决方案的工作方式是:

  • 过滤顶级列表并在每个数据项上调用 matchesEntryInTree,与 userEntry 进行比较
  • matchesEntryInTree 将检查每个数据项并查看它是数组还是对象
    • 如果 dataItem 是一个数组/对象,我们通过递归调用 matchesEntryInTree 再次钻取它们
    • 如果不是,我们调用 compareValues 来查看该条目是否与当前数据项匹配
  • 使用上面的递归模式,所有叶节点(无论树的形状如何)都将与初始 userEntry 进行比较

// test data for trial runs
const testData = [
  {
    id: 123488,
    dealerCode: "ACb3",
    name: "Some Name",
    gstin: "string",
    pan: "string",
    cin: "string",
    emails: [
      {
        name: "Some Email name",
        address: "anemail.domain.com",
        isPrimary: "boolean"
      }
    ],
    phoneNumbers: [
      {
        countryCode: "9398",
        number: "number",
        isPrimary: "boolean"
      }
    ],
    addresses: [
      {
        addressLine1: "Florida",
        addressLine2: "Street place",
        addressLine3: "string",
        country: "string",
        state: "string",
        city: "string",
        postalCode: "number",
        isPrimary: "boolean"
      }
    ],
    status: "string",
    statusId: "number"
  },
  {
    id: 88888,
    dealerCode: "NMC",
    name: "Some Other",
    gstin: "string",
    pan: "string",
    cin: "string",
    emails: [
      {
        name: "An Email thing",
        address: "athing.somewhere.org",
        isPrimary: "boolean"
      }
    ],
    phoneNumbers: [
      {
        countryCode: "93948",
        number: "number",
        isPrimary: "boolean"
      }
    ],
    addresses: [
      {
        addressLine1: "Denver",
        addressLine2: "Street place",
        addressLine3: "string",
        country: "string",
        state: "string",
        city: "string",
        postalCode: "number",
        isPrimary: "boolean"
      }
    ],
    status: "string",
    statusId: "number"
  }
];


// broke these into separate helper functions, but you can combine all of them except the recursive one if you'd like
const returnFilterResults = (userEntry, dataItems) => {
  const compareValues = (entry, dataValue) => {
    if ( _.isBoolean(dataValue)) {
      return entry === dataValue;
    } else if (_.isNumber(dataValue)) {
    // if the dataValue is a number, we convert both it and the user's entry (which probably already is a string) to a string to compare
    // you can make this comparison more strict if desired
      return _.includes(_.toLower(_.toString(dataValue)), _.toLower(entry));
    } else if (_.isString(dataValue)) {
      return _.includes(_.toLower(dataValue), _.toLower(entry));
    } else {
      return false;
    }
  };

  const matchesEntryInTree = (entry, dataItem) => {
  // if this dataItem is an object or array, let's drill back in again
    if (_.isObject(dataItem) || _.isArray(dataItem)) {
      // as we recursively move through the tree, check to see if *any* of the entries match, using 'some'
      return _.some(dataItem, innerDataItem => {
        return matchesEntryInTree(entry, innerDataItem);
      });
    } else {
    // if it's a primitive, then let's compare directly
      return compareValues(entry, dataItem);
    }
  };

  // I created a results variable so we could console log here in this snippet
  // but you can just return from the filter directly
  const results = _.filter(dataItems, dataItem => {
    return matchesEntryInTree(userEntry, dataItem);
  });

  console.log(userEntry, results);
  return results;
};

returnFilterResults("place", testData);
// both entries return

returnFilterResults("Denver", testData);
// second entry is returned

returnFilterResults(48, testData);
// both entries return - ID in first, countryCode in second

returnFilterResults(12, testData);
// first entry is returned
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

关于javascript - 如何在包含嵌套对象的对象数组中搜索值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53059175/

相关文章:

javascript - Lodash:没有最后一个元素的数组,没有突变

javascript - 未经授权的 Twitter 用户时间轴 json 请求

javascript - AngularJS - 一个页面上有更多相同的指令并具有自己的状态

javascript - Kendo 的 Angular 指令 : "kendo" is undefined?

angularjs - AngularJS中的未知提供者$ sce

javascript - 使用 Lodash 和 Moment 从单个日期按天/小时分组数组

javascript - 去抖一个带参数的函数

javascript 对象内容通过引用分配,这可能吗?

javascript - JQuery 不会将 css 规则应用于 IE 6 和 7 上的表格

javascript - 谷歌API翻译,只有一个div进入页面