简体   繁体   中英

How to map JSON key value pairs when object parent name is different each time?

I'm trying to map key value pairs (see investments in the below JSON) but the parent names are all different. It's proving difficult to search for the answer.

How do I go about targeting these data?

Many thanks in advance!

"quarterly": {
    "2021-12-31": {
            "date": "2021-12-31",
            "filing_date": "2022-01-28",
            "currency_symbol": "USD",
            "investments": "-12929000000.00"
    },
    "2021-09-30": {
            "date": "2021-09-30",
            "filing_date": "2021-10-29",
            "currency_symbol": "USD",
            "investments": "4608000000.00"
    },
    "2021-06-30": {
            "date": "2021-06-30",
            "filing_date": "2021-07-28",
            "currency_symbol": "USD",
            "investments": "5747000000.00"
    },
    "2021-03-31": {
            "date": "2021-03-31",
            "filing_date": "2021-04-29",
            "currency_symbol": "USD",
            "investments": "-7895000000.00"
    }, ...

Aiming to get something like this:

--------------------------------
|            |"Investment"     |
--------------------------------
|"2021-12-31"|"-12929000000.00"|
--------------------------------
|"2021-09-31"|"-4608000000.00" |
--------------------------------
|"2021-06-30"|"5747000000.00"  |
--------------------------------

Update

Since the OP updated the question providing more details I am refining my answer here.

Here is how I would write a function to get those data using Object.entries and Object.fromEntries .

There is also an alternative implementation as a single line to be used without calling another function.

const investmentsObject = (items) => {
  const investmentPairs = Object.entries(items).map(([key, {investments}]) => [key, investments])
  return Object.fromEntries(investmentPairs)
}

// As you can see the code is very compact, it can be used inline without the need of a new function too:

const inputItems = {} // ... items coming from some input

const investments = Object.fromEntries( Object.entries(inputItems).map( ([key, {investments}]) => [key, investments] ) )


// Below code is used to test the above function

const dataReport = {
  quarterly: {
    "2012-12-12": {
      investments: "-23.12345465",
      anotherKey: "test"
    },
    "2012-03-15": {
      investments: "-11.2345",
      anotherKey: "another test"
    }
  }
}

document.body.innerHTML = `<div>${JSON.stringify(investmentsObject(dataReport.quarterly), null, 2)}</div>`

Personal taste: I like much more this style than the old for loop implementation. I don't want to say this is better, it's just personal, even though code readability is important.

Actually, for loops are faster, they don't need an iterating function (even though arrow fns are performant) that means they don't push new calls in the stack, this can make a difference but only if you are iterating over tens or hundreds of thousands of values.

I think that it's easier to get something wrong in for loops, if cycling with an index you can get the stop condition wrong (ie: use of i <= items.length instead of <).

map and forEach do all the heavy lifting for you, they provide an index as second parameter and the full array of items as third to the iterating function.

I am a fun of functional programming, I enjoy Clojure(Script) and Rust (can't say I know it though), maybe that's why I enjoy so much the "new" javascript.


Original answer:

It's not very clear from your question what you need to do with the data.

You should explain what is the purpose of retrieving the investments data because you can use different approach.

If you need to manipulate the keys and the values on the fly (for example put them in a webpage in some format, you can use a combination of Object.entries and Array.forEach

Object.entries(items.quarterly).forEach(([itemKey, item], index) => {
    console.log(`Key at index ${index} is ${itemKey} and its value is ${item.investments}`)
})

If you need only the values, replace Object.entries with Object.values (for keys use Object.keys )

Object.values(items.quarterly).forEach((item, index) => { 
   console.log(`Value at index ${index} is ${item.investments}`)
})

Instead, if you need to save the data in an array use Object.entries (or Object.values or Object.keys , like above) and Array.map

const investments = Object.values(items.quarterly).map((item) => item.investments)

If you need to sum all the investments values to get the total you can use Object.values and Array.reduce

const investmentsTotal = Object.values(items.quarterly).reduce((tot, {investments}) => tot + parseFloat(investments), 0)

Here is a working example of the different approach

const el = document.querySelector("div")
const append = (text) => el.innerHTML += `${text}<br/>`
const items = {
    "quarterly": {
        "2021-12-31": {
            "date": "2021-12-31",
            "filing_date": "2022-01-28",
            "currency_symbol": "USD",
            "investments": "-12929000000.00"
        },
        "2021-09-30": {
            "date": "2021-09-30",
            "filing_date": "2021-10-29",
            "currency_symbol": "USD",
            "investments": "4608000000.00"
        },
        "2021-06-30": {
            "date": "2021-06-30",
            "filing_date": "2021-07-28",
            "currency_symbol": "USD",
            "investments": "5747000000.00"
        },
        "2021-03-31": {
            "date": "2021-03-31",
            "filing_date": "2021-04-29",
            "currency_symbol": "USD",
            "investments": "-7895000000.00"
        }
    }
};



append(`<b>Example 1</b>: Keys and values:`)

// 1) For keys and values use this forEach statement
Object.entries(items.quarterly).forEach(([itemKey, {investments}], index) => {
  append(`Key at index ${index} is ${itemKey} and its value is ${investments}`)
})
append("")


append(`<b>Example 2</b>: only investments values:`)

// 2) For only values use this forEach statement
Object.values(items.quarterly).forEach(({investments}, index) => {
  append(`Value at index ${index} is ${investments}`)
})
append("")

append(`<b>Example 3</b>: get the values for later usage`)
// 3) To get an array of the values use this map statement
const investmentsList = Object.values(items.quarterly).map(({investments}) => investments)
append(`investmentsList =>  ${JSON.stringify(investmentsList, null, 2)}`)
append("")

append(`<b>Example 4</b>: sum the investments values`)
// To sum all the values use this reduce statement
const investmentsTotal = Object.values(items.quarterly).reduce((tot, {investments}) => tot + parseFloat(investments), 0)
append(`Investments total = ${investmentsTotal}`)
append("")

append(`Done`)
<div></div>

In all the examples I used destructuration. All the functions receive the whole map where investments key is:

const item = {
    "date": "2021-12-31",
    "filing_date": "2022-01-28",
    "currency_symbol": "USD",
    "investments": "-12929000000.00"
}
// Instead of writing:
const investments = item.investments
// I used the destructuration:
const {investments} = item

Using it directly in the argument I didn't need to name the item variable, it was implicit.

bago's answer has a lot more detail, but I believe there is value in another brief explanation after your edit.

If we know how many levels deep the object is, and all the items will have the same keys, we can just loop through things. You could package this destructuring many different ways, and even get even spacing in a table printed to the console if that's your goal. However, I believe your main goal is to get the information to work with rather than explicitly putting it into a table.

I will break my code down line by line to further clarify.

  1. I initialize a variable named outputData and give it an initial value of an array which is going to contain arrays. At index 0 on initialization, there in an array which is acting kind of like a legend, or header row in excel.
  2. I use object destructuring assignment to quickly pull out the quarterly value from someObj
  3. I convert the quarterly object into entries . Basically an object {"foo": "bar"} becomes and array [ ["foo", "bar"] ] of key value pairs.
  4. I loop through these key value pairs again, using destructuring for the loop variable
  5. for each loop iteration I am pushing another array to the objectData array, that array following the same header pattern I started with
  6. After the loop I am just formatting the data into a human readable way (csv style)

If you have any questions please ask.

 const someObj = { "quarterly": { "2021-12-31": { "date": "2021-12-31", "filing_date": "2022-01-28", "currency_symbol": "USD", "investments": "-12929000000.00" }, "2021-09-30": { "date": "2021-09-30", "filing_date": "2021-10-29", "currency_symbol": "USD", "investments": "4608000000.00" }, "2021-06-30": { "date": "2021-06-30", "filing_date": "2021-07-28", "currency_symbol": "USD", "investments": "5747000000.00" } } }; const outputData = [ ["Key", "Investment", "filing_date", "currency_symbol"], ]; const {quarterly} = someObj; const entries = Object.entries(quarterly); for (const [key, val] of entries) { outputData.push([ key, val["investments"], val["filing_date"], val["currency_symbol"] ]); } const outputString = outputData.map(arr => arr.join(", ")).join("\n"); console.log(outputString);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM