Search code examples
javascript

Iterate through an array and copy certain values


I am working with the Toggl API and it is returning a JSON object containing data like so (edited to simplify)

{
    "user_id": 12345,
    "username": "JohnSmith",
    "description": "foo",
    "time_entries": [
      {
        "id": 3778920070,
        "seconds": 10800,
        "start": "2025-01-27T11:00:00+00:00",
        "stop": "2025-01-27T14:00:00+00:00",
        "at": "2025-01-27T16:17:24+00:00",
        "at_tz": "2025-01-27T16:17:24+00:00"
      }
    ],
    "row_number": 1
  },
{
    "user_id": 6789,
    "username": "JaneSmith",
    "description": "bar",
    "time_entries": [
      {
        "id": 3778684944,
        "seconds": 5977,
        "start": "2025-01-27T14:35:31+00:00",
        "stop": "2025-01-27T16:15:08+00:00",
        "at": "2025-01-27T16:15:09+00:00",
        "at_tz": "2025-01-27T16:15:09+00:00"
      }
    ],
    "row_number": 2
  },
{
    "user_id": 12345,
    "username": "JohnSmith",
    "description": "bar",
    "time_entries": [
      {
        "id": 3780521038,
        "seconds": 3600,
        "start": "2025-01-27T09:00:00+00:00",
        "stop": "2025-01-27T10:00:00+00:00",
        "at": "2025-01-28T13:34:31+00:00",
        "at_tz": "2025-01-28T13:34:31+00:00"
      }
    ],
    "row_number": 3
  },

I would like to read the array and copy a subsection of the data into a new array. Ideally the output would group matching usernames, for example

{
    "username": "JohnSmith",
    "description": "foo",
    "time_entries": [
      {
        "id": 3778920070,
        "seconds": 10800,
        "start": "2025-01-27T11:00:00+00:00",
        "stop": "2025-01-27T14:00:00+00:00",
        "at": "2025-01-27T16:17:24+00:00",
        "at_tz": "2025-01-27T16:17:24+00:00"
      },
      {
        "id": 3780521038,
        "seconds": 3600,
        "start": "2025-01-27T09:00:00+00:00",
        "stop": "2025-01-27T10:00:00+00:00",
        "at": "2025-01-28T13:34:31+00:00",
        "at_tz": "2025-01-28T13:34:31+00:00"
      }
    ],
    ],
    "row_number": 1
  },
{
    "user_id": 6789,
    "username": "JaneSmith",
    "description": "bar",
    "time_entries": [
      {
        "id": 3778684944,
        "seconds": 5977,
        "start": "2025-01-27T14:35:31+00:00",
        "stop": "2025-01-27T16:15:08+00:00",
        "at": "2025-01-27T16:15:09+00:00",
        "at_tz": "2025-01-27T16:15:09+00:00"
      }
    ],
    "row_number": 2
  },
{

In pseudo-code, I am thinking I should do something like

foreach element in array if key = username , push to new array if already exist only copy the time_entries key/value under that username

Is there a more elegant approach ?


Solution

  • You will need to group the entries by their user_id. After you have the grouped, grab the values and map them.

    During mapping, you can reassign the time_entries and give it a row_number based on the index.

    Note: The Map.groupBy was added to JavaScript in 2024, and it available in Chrome/Edge 117+, Firefox 119+, and Safari 17.4+. If this is not working, I included a polyfill below.

    // Polyfill for new Map.groupBy function.
    if (Map.groupBy === undefined) {
      Map.groupBy = function (data, keyFn) {
        if (typeof keyFn !== 'function') {
          throw new TypeError('keyFn must be a function');
        }
        return data.reduce((acc, item) => {
          const key = keyFn(item);
          return acc.set(key, acc.getOrDefault(key, []).concat(item));
        }, new Map());
      };
      Map.prototype.getOrDefault = function (key, defaultValue) {
        return this.has(key) ? this.get(key) : defaultValue;
      };
    }
    
    const data = getData();
    
    const grouped = [...Map.groupBy(data, user => user.user_id).values()]
      .map(([head, ...tail], index) => ({
        ...head,
        time_entries: [...head.time_entries, ...tail.flatMap(t => t.time_entries)],
        row_number: index + 1 // Might not be needed, do you need to reassign?
      }));
    
    console.log(grouped);
    
    function getData() {
      return [{
        "user_id": 12345,
        "username": "JohnSmith",
        "description": "foo",
        "time_entries": [{
          "id": 3778920070,
          "seconds": 10800,
          "start": "2025-01-27T11:00:00+00:00",
          "stop": "2025-01-27T14:00:00+00:00",
          "at": "2025-01-27T16:17:24+00:00",
          "at_tz": "2025-01-27T16:17:24+00:00"
        }],
        "row_number": 1
      }, {
        "user_id": 6789,
        "username": "JaneSmith",
        "description": "bar",
        "time_entries": [{
          "id": 3778684944,
          "seconds": 5977,
          "start": "2025-01-27T14:35:31+00:00",
          "stop": "2025-01-27T16:15:08+00:00",
          "at": "2025-01-27T16:15:09+00:00",
          "at_tz": "2025-01-27T16:15:09+00:00"
        }],
        "row_number": 2
      }, {
        "user_id": 12345,
        "username": "JohnSmith",
        "description": "bar",
        "time_entries": [{
          "id": 3780521038,
          "seconds": 3600,
          "start": "2025-01-27T09:00:00+00:00",
          "stop": "2025-01-27T10:00:00+00:00",
          "at": "2025-01-28T13:34:31+00:00",
          "at_tz": "2025-01-28T13:34:31+00:00"
        }],
        "row_number": 3
      }];
    }
    .as-console-wrapper { top: 0; max-height: 100% !important; }

    After running the snippet above, you should get the following output:

    [
      {
        "user_id": 12345,
        "username": "JohnSmith",
        "description": "foo",
        "time_entries": [
          {
            "id": 3778920070,
            "seconds": 10800,
            "start": "2025-01-27T11:00:00+00:00",
            "stop": "2025-01-27T14:00:00+00:00",
            "at": "2025-01-27T16:17:24+00:00",
            "at_tz": "2025-01-27T16:17:24+00:00"
          },
          {
            "id": 3780521038,
            "seconds": 3600,
            "start": "2025-01-27T09:00:00+00:00",
            "stop": "2025-01-27T10:00:00+00:00",
            "at": "2025-01-28T13:34:31+00:00",
            "at_tz": "2025-01-28T13:34:31+00:00"
          }
        ],
        "row_number": 1
      },
      {
        "user_id": 6789,
        "username": "JaneSmith",
        "description": "bar",
        "time_entries": [
          {
            "id": 3778684944,
            "seconds": 5977,
            "start": "2025-01-27T14:35:31+00:00",
            "stop": "2025-01-27T16:15:08+00:00",
            "at": "2025-01-27T16:15:09+00:00",
            "at_tz": "2025-01-27T16:15:09+00:00"
          }
        ],
        "row_number": 2
      }
    ]