0

I get something like this from the server:

{
  "2021-10-13": {
    "1. open": "141.2350",
    "2. high": "141.4000",
    "3. low": "139.2000",
    "4. close": "140.9100",
    "5. volume": "78762721"
  },
  "2021-10-12": {
    "1. open": "143.2300",
    "2. high": "143.2500",
    "3. low": "141.0401",
    "4. close": "141.5100",
    "5. volume": "73035859"
  },
  "2021-10-11": {
    "1. open": "142.2700",
    "2. high": "144.8100",
    "3. low": "141.8100",
    "4. close": "142.8100",
    "5. volume": "64452219"
  }
}

The key values may vary for different requests. How can I iterate over an object with unknown keys?

For example I want to write the second and third nested objects(stocks) inside the database but I don't know how should I specify them?

Hasani
  • 3,543
  • 14
  • 65
  • 125
  • Does this answer your question? [How do I loop through or enumerate a JavaScript object?](https://stackoverflow.com/questions/684672/how-do-i-loop-through-or-enumerate-a-javascript-object) – Evan Trimboli Mar 04 '22 at 03:18
  • `for( key in obj) { }` for...in loop is your friend https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in – Dan Oswalt Mar 04 '22 at 03:18
  • although, what do you mean you know you want the second and third nested items? object keys aren't going to be in an reliable order – Dan Oswalt Mar 04 '22 at 03:22
  • @DanOswalt: Yes, I am looking for a more accurate way to specify what I need. – Hasani Mar 04 '22 at 03:27

2 Answers2

1

Does this help?

type Keys = {
  "1. open": string;
  "2. high": string;
  "3. low": string;
  "4. close": string;
  "5. volume": string;
}
type B = {[Prop in keyof Keys]: Keys[Prop]}
type A = { [key: string]: B}
const result: A = {
  "2021-10-13": {
    "1. open": "141.2350",
    "2. high": "141.4000",
    "3. low": "139.2000",
    "4. close": "140.9100",
    "5. volume": "78762721"
  },
  "2021-10-12": {
    "1. open": "143.2300",
    "2. high": "143.2500",
    "3. low": "141.0401",
    "4. close": "141.5100",
    "5. volume": "73035859"
  },
  "2021-10-11": {
    "1. open": "142.2700",
    "2. high": "144.8100",
    "3. low": "141.8100",
    "4. close": "142.8100",
    "5. volume": "64452219"
  }
};
beautifulcoder
  • 10,832
  • 3
  • 19
  • 29
  • I can not understand how this statement works? `type A = { [key: string]: B};` – Hasani Mar 07 '22 at 18:58
  • 1
    @WDR It is an [index signature](https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures) and represents an object that has the type `B` at every property name that is a `string`. Another way to write that type using a [utility](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) would be `Record`. – jsejcksn Mar 07 '22 at 22:17
1

Here's an approach for parsing the data you're receiving and sorting it by newest date first:

TS Playground

const rawSummaryKeys = ['1. open', '2. high', '3. low', '4. close', '5. volume'] as const;
type RawSummaryKey = typeof rawSummaryKeys[number];

const parsedSummaryKeys = ['open', 'high', 'low', 'close', 'volume'] as const;
type ParsedSummaryKey = typeof parsedSummaryKeys[number];

const rawToParsedSummaryKeyMapping: Record<RawSummaryKey, ParsedSummaryKey> = {
  '1. open': 'open',
  '2. high': 'high',
  '3. low': 'low',
  '4. close': 'close',
  '5. volume': 'volume',
};

/** Values are parsable as numbers */
type RawSummary = Record<RawSummaryKey, string>;

/** Keys are dates in format: YYYY-MM-DD */
type DailyRawSummaries = Record<string, RawSummary>;

type ParsedSummary = Record<ParsedSummaryKey, number>;

function parseRawSummary (summary: RawSummary): ParsedSummary {
  const parsed = {} as ParsedSummary;
  for (const key of rawSummaryKeys) {
    // If the "volume" number ever exceeds Number.MAX_SAFE_INTEGER,
    // then you can switch to using BigInts
    parsed[rawToParsedSummaryKeyMapping[key]] = Number(summary[key]);
  }
  return parsed;
}

type DailySummaryEntrry = [date: string, summary: ParsedSummary];

function parseDailySummaries (summaries: DailyRawSummaries): DailySummaryEntrry[] {
  const entries: DailySummaryEntrry[] = [];

  for (const date in summaries) {
    const rawSummary = summaries[date];
    if (!rawSummary) continue;
    entries.push([date, parseRawSummary(rawSummary)]);
  }

  return entries.sort().reverse(); // sort by newest date first
}

function main () {
  const json = `{"2021-10-13":{"1. open":"141.2350","2. high":"141.4000","3. low":"139.2000","4. close":"140.9100","5. volume":"78762721"},"2021-10-12":{"1. open":"143.2300","2. high":"143.2500","3. low":"141.0401","4. close":"141.5100","5. volume":"73035859"},"2021-10-11":{"1. open":"142.2700","2. high":"144.8100","3. low":"141.8100","4. close":"142.8100","5. volume":"64452219"}}`;
  const raw: DailyRawSummaries = JSON.parse(json);

  const parsed = parseDailySummaries(raw);

  for (const [date, summary] of parsed) {
    console.log(date, summary);
  }
}

main();

Transpiled JS from TS playground:

const rawSummaryKeys = ['1. open', '2. high', '3. low', '4. close', '5. volume'];
const parsedSummaryKeys = ['open', 'high', 'low', 'close', 'volume'];
const rawToParsedSummaryKeyMapping = {
    '1. open': 'open',
    '2. high': 'high',
    '3. low': 'low',
    '4. close': 'close',
    '5. volume': 'volume',
};
function parseRawSummary(summary) {
    const parsed = {};
    for (const key of rawSummaryKeys) {
        // If the "volume" number ever exceeds Number.MAX_SAFE_INTEGER,
        // then you can switch to using BigInts
        parsed[rawToParsedSummaryKeyMapping[key]] = Number(summary[key]);
    }
    return parsed;
}
function parseDailySummaries(summaries) {
    const entries = [];
    for (const date in summaries) {
        const rawSummary = summaries[date];
        if (!rawSummary)
            continue;
        entries.push([date, parseRawSummary(rawSummary)]);
    }
    return entries.sort().reverse(); // sort by newest date first
}
function main() {
    const json = `{"2021-10-13":{"1. open":"141.2350","2. high":"141.4000","3. low":"139.2000","4. close":"140.9100","5. volume":"78762721"},"2021-10-12":{"1. open":"143.2300","2. high":"143.2500","3. low":"141.0401","4. close":"141.5100","5. volume":"73035859"},"2021-10-11":{"1. open":"142.2700","2. high":"144.8100","3. low":"141.8100","4. close":"142.8100","5. volume":"64452219"}}`;
    const raw = JSON.parse(json);
    const parsed = parseDailySummaries(raw);
    for (const [date, summary] of parsed) {
        console.log(date, summary);
    }
}
main();
jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • What does `[number]` mean in `type RawSummaryKey = typeof rawSummaryKeys[number];` statement? – Hasani Mar 07 '22 at 19:01
  • 1
    @WDR It is an [indexed access type](https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html#handbook-content) on the tuple: so it represents a union of each of the types within. You can see a representation of that type when you hover over the type name in an environment with IntelliSense (like the playground). – jsejcksn Mar 07 '22 at 22:11
  • Thank you very much for your helps. May you also say what does the `Number` function do? here: `Number(summary[key]);` – Hasani Mar 11 '22 at 00:13
  • @WDR What do you think it does? – jsejcksn Mar 11 '22 at 01:01
  • It seems it's just a type caster but I am not sure. – Hasani Mar 11 '22 at 01:11
  • @WDR [You got it](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number). – jsejcksn Mar 11 '22 at 01:44
  • In your code, when I print the `entries` the result is like `[ [ "2022-03-11", { open: "158.9300", high: "159.2800", low: "154.5000", close: "154.7300", volume: "96970102" } ], [ "2022-03-10", { open: "160.2000", high: "160.3900", low: "155.9800", close: "158.5200", volume: "105342033" } ], ]` That is an array of arrays of object. – Hasani Mar 13 '22 at 09:01
  • How can I convert it to object of objects like `{ "2022-03-11" : { open: "158.9300", high: "159.2800", low: "154.5000", close: "154.7300", volume: "96970102" } , "2022-03-10" : { open: "160.2000", high: "160.3900", low: "155.9800", close: "158.5200", volume: "105342033" } }` – Hasani Mar 13 '22 at 09:05
  • @WDR Objects are not ordered in JS/TS. which is why I provided an order-sensitive, iterable structure (`entries`) in the answer. – jsejcksn Mar 13 '22 at 09:07
  • That's right, but how should I write them into the MongoDB? Isn't it better to convert them to an object like my previous comment, then write it to DataBase? – Hasani Mar 13 '22 at 09:10
  • Actually I want to add another key to this result as the name of the stock and after that, it becomes something like this `[ [ "AAPL", [ [ "2022-03-11", [Object] ], [ "2022-03-10", [Object] ], ], ]` That I don't know if is it good idea to save them like this? It seems a redundency of `[` array to array `]` – Hasani Mar 13 '22 at 09:14
  • And if I want to add another stock it becomes like: `[ "MSFT", [ [ "2022-03-11", [Object] ], [ "2022-03-10", [Object] ], ], ], [ "AAPL", [ [ "2022-03-11", [Object] ], [ "2022-03-10", [Object] ], ], ]` Is it efficient to save it like this into a MongoDB database? – Hasani Mar 13 '22 at 09:17
  • @WDR Please take [the tour](https://stackoverflow.com/tour) of Stack Overflow, and check out the help center section about [asking questions](https://stackoverflow.com/help/asking). Each question should focus on a single problem. I have answered the question that you asked, and you are free to [ask a new question](https://stackoverflow.com/questions/ask) if you have another question (e.g. about how to use data structures in your program). – jsejcksn Mar 13 '22 at 09:43
  • I tried to do some modification and created a new question here. I appreciate your help also there: https://stackoverflow.com/questions/71462277/how-can-i-store-my-data-into-the-mongodb – Hasani Mar 14 '22 at 02:09