admin管理员组

文章数量:1417425

I have below this nested object

I need to create an array using this object containing keys. And if keys are object then it should use .dot syntax. and if it is an array then it should give me key.0.keyName. Is it possible to do so?

Output

[
  "AllowIPNPayment",
  "AllowOnlinePayment",
  "MetaData.CreateTime",
  "MetaData.LastUpdatedTime",
  "CustomField.0.DefinitionId",
  "CustomField.0.Name",
  "CustomField.0.Type",
  ...
]

What I have tried is just ugly and does give me expected result. If it is possible with more concise way.

const invoiceObject = { "AllowIPNPayment": false, "AllowOnlinePayment": false, "AllowOnlineCreditCardPayment": false, "AllowOnlineACHPayment": false, "domain": "QBO", "sparse": false, "Id": "16", "SyncToken": "1", "MetaData": { "CreateTime": "2020-03-25T15:10:40-07:00", "LastUpdatedTime": "2020-03-26T11:06:49-07:00" }, "CustomField": [{ "DefinitionId": "1", "Name": "Crew #", "Type": "StringType" }], "DocNumber": "1007", "TxnDate": "2020-03-03", "CurrencyRef": { "value": "USD", "name": "United States Dollar" }, "LinkedTxn": [{ "TxnId": "32", "TxnType": "Payment" }], "Line": [{ "Id": "1", "LineNum": 1, "Description": "Custom Design", "Amount": 750, "DetailType": "SalesItemLineDetail", "SalesItemLineDetail": { "ItemRef": { "value": "4", "name": "Design" }, "UnitPrice": 75, "Qty": 10, "TaxCodeRef": { "value": "NON" } } }, { "Amount": 750, "DetailType": "SubTotalLineDetail", "SubTotalLineDetail": {} } ], "TxnTaxDetail": { "TotalTax": 0 }, "CustomerRef": { "value": "13", "name": "uiool" }, "CustomerMemo": { "value": "Thank you for your business and have a great day!" }, "SalesTermRef": { "value": "3" }, "DueDate": "2020-04-02", "TotalAmt": 750, "ApplyTaxAfterDiscount": false, "PrintStatus": "NeedToPrint", "EmailStatus": "NotSet", "BillEmail": { "Address": "uiikoool" }, "Balance": 450 }

let object = {}
for (let k in invoiceObject) {
  if (typeof invoiceObject[k] === "object") {
    object[k] = {};
    for (let l in invoiceObject[k]) {
      object[k][l] = "";
    }
  } else if (typeof invoiceObject[k] === "array") {
    object[k] = [];
    for (let l in invoiceObject[k][0]) {
      object[k][l] = "";
    }
  } else {
    object[k] = "";
  }
}
console.log(object)

I have below this nested object

I need to create an array using this object containing keys. And if keys are object then it should use .dot syntax. and if it is an array then it should give me key.0.keyName. Is it possible to do so?

Output

[
  "AllowIPNPayment",
  "AllowOnlinePayment",
  "MetaData.CreateTime",
  "MetaData.LastUpdatedTime",
  "CustomField.0.DefinitionId",
  "CustomField.0.Name",
  "CustomField.0.Type",
  ...
]

What I have tried is just ugly and does give me expected result. If it is possible with more concise way.

const invoiceObject = { "AllowIPNPayment": false, "AllowOnlinePayment": false, "AllowOnlineCreditCardPayment": false, "AllowOnlineACHPayment": false, "domain": "QBO", "sparse": false, "Id": "16", "SyncToken": "1", "MetaData": { "CreateTime": "2020-03-25T15:10:40-07:00", "LastUpdatedTime": "2020-03-26T11:06:49-07:00" }, "CustomField": [{ "DefinitionId": "1", "Name": "Crew #", "Type": "StringType" }], "DocNumber": "1007", "TxnDate": "2020-03-03", "CurrencyRef": { "value": "USD", "name": "United States Dollar" }, "LinkedTxn": [{ "TxnId": "32", "TxnType": "Payment" }], "Line": [{ "Id": "1", "LineNum": 1, "Description": "Custom Design", "Amount": 750, "DetailType": "SalesItemLineDetail", "SalesItemLineDetail": { "ItemRef": { "value": "4", "name": "Design" }, "UnitPrice": 75, "Qty": 10, "TaxCodeRef": { "value": "NON" } } }, { "Amount": 750, "DetailType": "SubTotalLineDetail", "SubTotalLineDetail": {} } ], "TxnTaxDetail": { "TotalTax": 0 }, "CustomerRef": { "value": "13", "name": "uiool" }, "CustomerMemo": { "value": "Thank you for your business and have a great day!" }, "SalesTermRef": { "value": "3" }, "DueDate": "2020-04-02", "TotalAmt": 750, "ApplyTaxAfterDiscount": false, "PrintStatus": "NeedToPrint", "EmailStatus": "NotSet", "BillEmail": { "Address": "uiikoool" }, "Balance": 450 }

let object = {}
for (let k in invoiceObject) {
  if (typeof invoiceObject[k] === "object") {
    object[k] = {};
    for (let l in invoiceObject[k]) {
      object[k][l] = "";
    }
  } else if (typeof invoiceObject[k] === "array") {
    object[k] = [];
    for (let l in invoiceObject[k][0]) {
      object[k][l] = "";
    }
  } else {
    object[k] = "";
  }
}
console.log(object)

Share Improve this question edited May 19, 2020 at 18:05 Dark Knight asked May 19, 2020 at 11:01 Dark KnightDark Knight 1,0936 gold badges26 silver badges50 bronze badges 5
  • Yes it is possible. What did you try so far and what were the issues with that code you tried? – mplungjan Commented May 19, 2020 at 11:04
  • Updated @mplungjan. Pls check – Dark Knight Commented May 19, 2020 at 11:07
  • Please don't reinvent this unless you are doing it for an academic purpose: npmjs./package/flat – Adam Jenkins Commented May 19, 2020 at 11:20
  • 1 Does this answer your question? Best way to flatten JS object (keys and values) to a single depth array – Snow Commented May 19, 2020 at 11:35
  • 1 I might get better answer here. And most of the answer from the duplicate one is not what I expected. Also I have used lodash tag here. @Snow – Dark Knight Commented May 19, 2020 at 11:44
Add a ment  | 

3 Answers 3

Reset to default 3

You can create a recursive function (getSchema) that checks if a value (val) is an object (arrays included), iterate it with _.flatMap(), and collects the keys until it hits a value which is not an object. It then joins the collected keys and returns the string.

const getSchema = (val, keys = []) =>
  _.isObject(val) ? // if it's an object or array
    _.flatMap(val, (v, k) => getSchema(v, [...keys, k])) // iterate it and call fn with the value and the collected keys
    :
    keys.join('.') // return the joined keys

const invoiceObject = { "AllowIPNPayment": false, "AllowOnlinePayment": false, "AllowOnlineCreditCardPayment": false, "AllowOnlineACHPayment": false, "domain": "QBO", "sparse": false, "Id": "16", "SyncToken": "1", "MetaData": { "CreateTime": "2020-03-25T15:10:40-07:00", "LastUpdatedTime": "2020-03-26T11:06:49-07:00" }, "CustomField": [{ "DefinitionId": "1", "Name": "Crew #", "Type": "StringType" }], "DocNumber": "1007", "TxnDate": "2020-03-03", "CurrencyRef": { "value": "USD", "name": "United States Dollar" }, "LinkedTxn": [{ "TxnId": "32", "TxnType": "Payment" }], "Line": [{ "Id": "1", "LineNum": 1, "Description": "Custom Design", "Amount": 750, "DetailType": "SalesItemLineDetail", "SalesItemLineDetail": { "ItemRef": { "value": "4", "name": "Design" }, "UnitPrice": 75, "Qty": 10, "TaxCodeRef": { "value": "NON" } } }, { "Amount": 750, "DetailType": "SubTotalLineDetail", "SubTotalLineDetail": {} } ], "TxnTaxDetail": { "TotalTax": 0 }, "CustomerRef": { "value": "13", "name": "uiool" }, "CustomerMemo": { "value": "Thank you for your business and have a great day!" }, "SalesTermRef": { "value": "3" }, "DueDate": "2020-04-02", "TotalAmt": 750, "ApplyTaxAfterDiscount": false, "PrintStatus": "NeedToPrint", "EmailStatus": "NotSet", "BillEmail": { "Address": "uiikoool" }, "Balance": 450 }

const result = getSchema(invoiceObject)

console.log(result)
<script src="https://cdnjs.cloudflare./ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Without lodash, the main change is to use Object.entries() to get an array of [key, value] pairs, since Array.flatMap() can't iterate objects:

const getSchema = (val, keys = []) =>
  typeof val === 'object' && val !== null ? // if it's an object or array
    Object.entries(val) // get [key, value] pairs of object/array
      .flatMap(([k, v]) => getSchema(v, [...keys, k])) // iterate it and call fn with the value and the collected keys 
    :
    keys.join('.') // return the joined keys

const invoiceObject = { "AllowIPNPayment": false, "AllowOnlinePayment": false, "AllowOnlineCreditCardPayment": false, "AllowOnlineACHPayment": false, "domain": "QBO", "sparse": false, "Id": "16", "SyncToken": "1", "MetaData": { "CreateTime": "2020-03-25T15:10:40-07:00", "LastUpdatedTime": "2020-03-26T11:06:49-07:00" }, "CustomField": [{ "DefinitionId": "1", "Name": "Crew #", "Type": "StringType" }], "DocNumber": "1007", "TxnDate": "2020-03-03", "CurrencyRef": { "value": "USD", "name": "United States Dollar" }, "LinkedTxn": [{ "TxnId": "32", "TxnType": "Payment" }], "Line": [{ "Id": "1", "LineNum": 1, "Description": "Custom Design", "Amount": 750, "DetailType": "SalesItemLineDetail", "SalesItemLineDetail": { "ItemRef": { "value": "4", "name": "Design" }, "UnitPrice": 75, "Qty": 10, "TaxCodeRef": { "value": "NON" } } }, { "Amount": 750, "DetailType": "SubTotalLineDetail", "SubTotalLineDetail": {} } ], "TxnTaxDetail": { "TotalTax": 0 }, "CustomerRef": { "value": "13", "name": "uiool" }, "CustomerMemo": { "value": "Thank you for your business and have a great day!" }, "SalesTermRef": { "value": "3" }, "DueDate": "2020-04-02", "TotalAmt": 750, "ApplyTaxAfterDiscount": false, "PrintStatus": "NeedToPrint", "EmailStatus": "NotSet", "BillEmail": { "Address": "uiikoool" }, "Balance": 450 }

const result = getSchema(invoiceObject)

console.log(result)

inspired by the answer given in this post and understanding you just want to get the property-names, not values, you could do it like this. sorry, this uses plain javascript.

function flattenObjectToKeyArray(ob) {
  var toReturn = [];
  for (var prop in ob) {
    if (!ob.hasOwnProperty(prop)) continue;

    if ((typeof ob[prop]) == 'object' && ob[prop] !== null) {
      var flatObject = flattenObjectToKeyArray(ob[prop]);
      for (var idx = 0; idx < flatObject.length; idx++) {
        toReturn.push(prop + '.' + flatObject[idx]);
      }
    } else {
      toReturn.push(prop);
    }
  }
  return toReturn;
}

You could solve this with a recursive function. The function below keeps track of the current keys, and joins them as soon as an end point is reached (a non-object or empty object/array).

const invoiceObject = { "AllowIPNPayment": false, "AllowOnlinePayment": false, "AllowOnlineCreditCardPayment": false, "AllowOnlineACHPayment": false, "domain": "QBO", "sparse": false, "Id": "16", "SyncToken": "1", "MetaData": { "CreateTime": "2020-03-25T15:10:40-07:00", "LastUpdatedTime": "2020-03-26T11:06:49-07:00" }, "CustomField": [{ "DefinitionId": "1", "Name": "Crew #", "Type": "StringType" }], "DocNumber": "1007", "TxnDate": "2020-03-03", "CurrencyRef": { "value": "USD", "name": "United States Dollar" }, "LinkedTxn": [{ "TxnId": "32", "TxnType": "Payment" }], "Line": [{ "Id": "1", "LineNum": 1, "Description": "Custom Design", "Amount": 750, "DetailType": "SalesItemLineDetail", "SalesItemLineDetail": { "ItemRef": { "value": "4", "name": "Design" }, "UnitPrice": 75, "Qty": 10, "TaxCodeRef": { "value": "NON" } } }, { "Amount": 750, "DetailType": "SubTotalLineDetail", "SubTotalLineDetail": {} } ], "TxnTaxDetail": { "TotalTax": 0 }, "CustomerRef": { "value": "13", "name": "uiool" }, "CustomerMemo": { "value": "Thank you for your business and have a great day!" }, "SalesTermRef": { "value": "3" }, "DueDate": "2020-04-02", "TotalAmt": 750, "ApplyTaxAfterDiscount": false, "PrintStatus": "NeedToPrint", "EmailStatus": "NotSet", "BillEmail": { "Address": "uiikoool" }, "Balance": 450 };

function getDotKeys(item, keys = []) {
  const isObject = item && typeof item == "object";
  if (!isObject) return Array.of(keys.join("."));

  const pairs = Array.isArray(item)
    ? item.map((value, index) => [index, value])
    : Object.entries(item);

  const isEmpty = !pairs.length;
  if (isEmpty) return Array.of(keys.join("."));

  const result = [];
  for (const [key, value] of pairs) {
    const dotKeys = getDotKeys(value, [...keys, key]);
    result.push(...dotKeys);
  }
  return result;
}

console.log(getDotKeys(invoiceObject));

This does produce a different result than what you have in your question, since your solution stops at the second level for objects and third level for arrays. This solution also includes more then only index 0.

本文标签: javascriptFlatten an object using lodashStack Overflow