Как искать значение в массиве объектов, которые содержат вложенные объекты - PullRequest
0 голосов
/ 30 октября 2018

Я хочу разработать эту функцию для поиска / фильтрации списка. По сути, я получаю поисковый термин из поля ввода, а затем мне нужно получить все элементы, которые включают поисковый термин, из массива.

Вот что я пробовал до сих пор: он работает для свойств корневого уровня, но не работает с вложенными массивами / объектами:

// 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.

Ответы [ 2 ]

0 голосов
/ 31 октября 2018

Для такой проблемы, когда вам нужно сравнить гетерогенный список примитивов и объектов / массивов, обычно лучше всего использовать рекурсивную именованную функцию. Вероятно, это должно решить то, что вы ищете, исходя из следующих предположений:

  1. Все записи пользователя рассматриваются как строки. Так что 99 и «99» - это одно и то же. Я прокомментирую в коде, где это предположение сделано
  2. Все записи не чувствительны к регистру (все преобразованы в нижний регистр)
  3. Нет заданной глубины вложенных объектов / массивов; решение ниже работает рекурсивно для любой глубины гетерогенного списка
  4. Если что-либо совпадает в любом листовом узле, весь объект будет возвращен

Ниже описано решение:

  • Отфильтруйте список верхнего уровня и вызовите matchEntryInTree для каждого элемента данных, по сравнению с userEntry
  • matchEntryInTree проверит каждый элемент dataItem и выяснит, является ли он массивом или объектом.
    • Если dataItem является массивом / объектом, мы снова углубляемся в них, рекурсивно вызывая matchEntryInTree
    • Если это не так, мы вызываем 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>
0 голосов
/ 30 октября 2018

Почему бы вам не использовать функцию сглаживания, чтобы сгладить ваш объект / JSON, а затем найти свое значение? Примером этого является следующее:

var flattenObject = function(ob) {
    var toReturn = {};

    for (var i in ob) {
        if (!ob.hasOwnProperty(i)) continue;

        if ((typeof ob[i]) == 'object') {
            var flatObject = flattenObject(ob[i]);
            for (var x in flatObject) {
                if (!flatObject.hasOwnProperty(x)) continue;

                toReturn[i + '.' + x] = flatObject[x];
            }
        } else {
            toReturn[i] = ob[i];
        }
    }
    return toReturn;
};

Для вложенного объекта, скажем,

{
  A : {
    B: {
     C: "V"
    }
  }
}

вы получите объект с ключом A.B.C и значением "V". Таким образом, у вас будет только один уровень для поиска вашего значения.

Надеюсь, это поможет! Ура!

...