Condense items into objects and arrays when the Express query yields multiple objects in a many-to-many query

I have a situation where my SQL queries are returning multiple objects due to a many-to-many mapping in express. I am in search of a tool that can help me simplify these common objects by nesting arrays of objects within them.

SELECT *
FROM User
LEFT JOIN UserAddress ON UserAddress.user_id = User.user_id
LEFT JOIN Address ON Address.address_id = UserAddress.address_id;

When we execute this query, the response looks something like:

[
  {
    "user_id": 1,
    "name": "test name",
    "address_id": 1,
    "address": "1234 address way"
  },
  {
    "user_id": 1,
    "name": "test name",
    "address_id": 2,
    "address": "5678 new address"
  },
  {
    "user_id": 2,
    "name": "diff name",
    "address_id": 1,
    "address": "1234 address way"
  }
]

What I am aiming for is to transform this array using a JavaScript tool so it resembles the following structure:

[
  {
    "user_id": 1,
    "name": "test name",
    "address": [
      {
        "address_id": 1,
        "address": "1234 address way"
      },
      {
        "address_id": 2,
        "address": "5678 new address"
      }
    ]
  },
  {
    "user_id": 2,
    "name": "diff name",
    "address_id": 1,
    "address": "1234 address way"
  }
]

However, I am uncertain about what this transformation process is formally called or if there exists a specific tool that can facilitate this operation?

Answer №1

Here is a versatile solution that allows you to specify the data you want to collate and how you want it structured. This function utilizes a Map object to assist in organizing the data based on a specified key (user_id in this case):

function collateData(sourceArray, keyName, collectionName, collectionFields) {
    let collection = new Map();
    for (let item of sourceArray) {
        let targetObj = collection.get(item[keyName]);
        if (!targetObj) {
            targetObj = Object.assign({}, item);
            // Remove the properties being collected
            for (let field of collectionFields) {
                delete targetObj[field];
            }
            targetObj[collectionName] = [];
            collection.set(item[keyName], targetObj);
        }
        
        let collectedObj = {};
        // Copy over the fields being collected
        for (let field of collectionFields) {
            collectedObj[field] = item[field];
        }
        
        targetObj[collectionName].push(collectedObj);
    }
    
    // Convert Map to final array
    return Array.from(collection.values());
}

This approach always stores address fields in an array format for consistency, even when there's only one address (resulting in an array of length 1). This simplifies data handling for users as they can reliably access the address information without complex logic to handle differing formats.

An example usage scenario with sample data:

let data = [
  {
    "user_id": 1,
    "name": "test name",
    "address_id": 1,
    "address": "1234 address way"
  },
  {
    "user_id": 1,
    "name": "test name",
    "address_id": 2,
    "address": "5678 new address"
  },
  {
    "user_id": 2,
    "name": "diff name",
    "address_id": 1,
    "address": "1234 address way"
  }
];

let result = collateData(data, "user_id", "address", ["address", "address_id"]);
console.log(result);

If you prefer not to have single-element arrays, you can perform post-processing to extract individual elements:

// Function remains the same, only slight adjustment at the end for post-processing

let result = collateData(data, "user_id", "address", ["address", "address_id"]);

// Post-processing to remove single-element arrays
for (let item of result) {
    let array = item["address"];
    if (array.length === 1) {
        delete item["address"]; // Remove array
        Object.assign(item, array[0]); // Copy fields back to main object
    }
}

console.log(result);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error: Unable to locate module adaptivecards-templating

After adding the module through the command: npm install adaptive-expressions adaptivecards-templating --save and importing it, I encountered an error when trying to run my application: ...

"Vue is failing to actively update an input that relies on changes from another

I am working on a project where the selected country automatically determines the phone country code. I have set it up so that when I change the country, the corresponding country code should update as well. Within a customer object, both the country and ...

The concept of Puppeteer involves defining the browser and page in a synchronous manner

In the beginning of the Puppeteer tutorial, it is instructed to follow this code snippet: const puppeteer = require('puppeteer'); (async () => { await page.goto('https://example.com'); const browser = await puppeteer.launch ...

What could be causing this issue where the call to my controller is not functioning properly?

Today, I am facing a challenge with implementing JavaScript code on my view in MVC4 project. Here is the snippet of code that's causing an issue: jQuery.ajax({ url: "/Object/GetMyObjects/", data: { __RequestVerificationToken: jQuery(" ...

How can we stop the constant fluctuation of the last number on a number input field with a maxLength restriction

In my ReactJS code, I have an input field that looks like this: <input type="number" onKeyPress={handleKeyPress} maxLength={3} min="0" max="999" step=".1" onPaste={handlePaste} /> Below are the functions associated w ...

Is it possible to utilize Jquery in order to add an opening <tr> tag and a closing </tr> tag within a dynamic table?

I have been experimenting with the code snippet below in an attempt to dynamically add a closing tag followed by an opening tag after every three cells, essentially creating a new row. Progress has been made as the DOM inspector shows a TR node; h ...

Material Design Autocomplete search feature in Angular 2

I'm encountering some challenges with autocomplete in Angular2 Material Design. Here are the issues I'm facing: 1. When I type a character that matches what I'm searching for, it doesn't display in the autocomplete dropdown as shown in ...

Managing mouse click events in jQuery

Here on SO, I successfully implemented the mouse down handler. Now, my goal is to separate these functions into their own file named test.js. Within test.js, the code looks like this: function handleMouseDown(e) { console.log('handleMouseDown&ap ...

The attempt to add a note with a POST request to the /api/notes/addnote endpoint resulted in a

I'm facing an issue while trying to send a POST request to the /api/notes/addnote endpoint. The server is returning a 404 Not Found error. I have checked the backend code and made sure that the endpoint is correctly defined. Here are the specifics of ...

Difficulties encountered when trying to load liquid using Javascript

Having trouble loading the Shopify liquid object {{product.price | json}} into JS, as it's displaying NaN with the current code on the front end. Any suggestions on how to properly pass liquid into JS? I've tried two functions but neither seem t ...

Iteratively traverse the object to establish connections between parent and child elements

I've been working on creating a recursive function with some guidance I received here: looping through an object (tree) recursively The goal is to traverse the object 'o' and generate a new one where each key is associated with its parents ...

As I iterated over the Vehicles API data, I encountered an issue while trying to access specific Vehicle information. I received an error message stating "Cannot read property 'id' of undefined

Exploring the realms of Angular, with some experience in older versions, I find myself faced with a challenge involving two APIs - "/vehicles" and "/vehicle/{id}". The process involves fetching data from "/vehicles", iterating through it to match IDs, the ...

Calculating the total of an array's values using JavaScript

Receiving information from an API and looking to aggregate the values it contains. Consider the following code snippet: function totalPesos(){ $http.get('/api/valueForTest') .then(function(data){ $scope.resumePesos = data.data.Re ...

The Consequences of Using Undeclared Variables in JavaScript

What is the reason behind JavaScript throwing a reference error when attempting to use an undeclared variable, but allowing it to be set to a value? For example: a = 10; //creates global variable a and sets value to 10 even though it's undeclared al ...

Would it be considered improper to implement an endless loop within a Vue.js instance for the purpose of continuously generating predictions with Tensorflow.js?

In my project, I am leveraging different technologies such as Tensorflow.js for training and predicting foods, Web API to access the webcam in my notebook, and Vue.js to create a simple web page. Within the addExample() method, there is an infinite loop r ...

Tips for building a versatile fetch function that can be reused for various JSON formats within a React application

Using the fetch method in various components: fetch(url) .then(result => { if (!result.ok) { throw new Error("HTTP error " + result.status) } return result.json() }) .then(result => { ...

Printing Multiple Pages Using JavaScript and Cascading Style Sheets

Encountering difficulties displaying page numbers when printing multiple multipage reports Here is the provided HTML Format : <style type="text/css> body { counter-reset: report 1 page 0; } td.footer:after { counter-increment: page; content ...

The instance of the Javascript Object Prototype losing its reference

I'm currently developing a small Javascript Object that will attach click listeners to specific elements, triggering an AJAX call to a PHP function. Everything is functioning as expected, but I want to execute a function once the AJAX response is rece ...

Error: The route cannot be established within an asynchronous function

The following code snippet is from the api.js module, responsible for creating a test route: 'use strict'; module.exports = function (app) { console.log("before route creation"); app.get("/api/test", (req, res) => { ...

I am encountering a problem with the app.patch() function not working properly. Although the get and delete functions are functioning as expected, the patch function seems to be

I am in the process of setting up a server that can handle CRUD operations. The Movie model currently only consists of one property, which is the title. Although I can create new movies, delete existing ones, and even search for a ...