Transforming an array into an object by applying filtering, prioritization, and minimizing loops

Consider the following array:

const array = [{
  typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, {
  typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
  typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}]

The desired output should be:

{
  welcome: {
    landscape: ['Welcome'],
    portrait: ['Bonjour bonjour']
  }
}

To achieve this, we need to convert the array into an object structure like

{typeName: {orientation: value[]}}
, as shown below:

// This is NOT the final output, it's an intermediate step -- keep reading
{
  welcome: {
    landscape: ['Welcome', 'Bonjour'],
    portrait: ['Bonjour bonjour']
  }
}

However, there is a requirement for prioritization: if languageId=1 is present in the array, then ignore all other values with the same typeName and orientation. In the given example, only ['Welcome'] should be included since its languageId is 1, meaning 'Bonjour' can be ignored. If languageId=1 is missing, any value can be added (in this case, welcome.portrait).

The conversion process is straightforward using the .reduce() method as demonstrated by the code snippet provided.

However, dealing with prioritization while avoiding nested loops poses a challenge. The current solution involves filtering the array with an inner loop to check for conflicting types. While this works without issues, it may impact performance when handling large arrays.

Therefore, the main question remains: what is the most efficient approach to eliminate the need for inner loops?

Answer №1

If you want to prioritize certain items and avoid adding duplicates to the array, consider using a Set.

const
    array = [{ typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome' }, { typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour' }, { typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour' }],
    hasPrio = new Set,
    result = array.reduce((r, { typeName, orientation, languageId, value }) => {
        var key = `${typeName}|${orientation}`;

        if (!r[typeName]) r[typeName] = {};
        if (languageId === 1) {
            r[typeName][orientation] = [value];
            hasPrio.add(key);
        } else if (!hasPrio.has(key)) {
            if (!r[typeName][orientation]) r[typeName][orientation] = [];
            r[typeName][orientation].push(value);
        }
        return r;
    }, {});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Answer №2

To keep track of whether a languageId === 1 for a specific combination of typeName and orientation, you can utilize a Set. According to the specification, a Set is designed to have sublinear performance:

Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection.

Therefore, using a Set would offer better performance compared to an inner loop. You could also opt for an object if the keys are strings, as demonstrated below. If this approach is chosen, it's recommended to create the object with Object.create(null) to prevent inheritance from Object.prototype.

Instead of utilizing reduce, you can achieve the desired outcome with a single straightforward loop:

const array = [{
  typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, { typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Bon dia'
}, {
  typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
  typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}]

const sawPriority = new Set();
const result = {};
for (const {typeName, orientation, languageId, value} of array) {
    const key = `${typeName}-${orientation}`;
    const isPriority = languageId === 1;
    let entry = result[typeName];
    if (!entry) {
        // No entry yet, easy peasy
        entry = result[typeName] = {[orientation]: [value]};
        if (isPriority) {
            sawPriority.add(key);
        }
    } else {
        const hasPriority = sawPriority.has(key);
        if (hasPriority === isPriority) {
            // Either the first for a new orientation, or a subsequent entry that
            // matches the priority
            const inner = entry[orientation];
            if (inner) {
                // Subsequent, add
                inner.push(value);
            } else {
                // First for orientation, create
                entry[orientation] = [value];
            }
        } else if (isPriority) {
            // It's a new priority entry, overwrite
            entry[orientation] = [value];
            sawPriority.add(key);
        }
    }
}

console.log(result)

(You mentioned in a comment that multiple entries where languageId === 1 should be aggregated into an array, so I have included a second one in the example above to showcase this functionality.)


In the provided code snippet, I'm comparing two boolean values like this:

if (hasPriority === isPriority) {

This comparison works because I am certain that both variables are indeed booleans, not just truthy or falsy values. This certainty arises from the fact that isPriority results from a strict equality comparison (===) and hasPriority comes from invoking the has method on a Set instance, which guarantees a boolean return value.

If there were any doubt regarding whether one of them might be a truthy or falsy value rather than explicitly true or false, I would ensure they are both booleans by adding ! before them in the comparison:

if (!hasPriority === !isPriority) {

Answer №3

Check out the interactive demonstration here

const data = [{
  typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, {
  typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
  typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}];


let object = data.reduce( (accumulator, {orientation, value})=>{   
      accumulator[orientation] = (accumulator[orientation] || []).concat(value);
      return accumulator;
   }, {});

result = {};
result[data[0].typeName] = 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

The route parameters seem to be malfunctioning when using the Google Maps Directions API

Currently, I am attempting to transfer the latitude and longitude of a location from one HTML file to another using the $routeParams feature. In the second HTML file, I utilized the Google Maps directions API to display the directions from the source lati ...

Assistance requested with Javascript for an HTML dropdown menu

My question involves utilizing two HTML drop down menus. <select> <option value="">Please Select</option> <option value="volvo">Volvo</option> <option value="saab">Saab</option> <option ...

The JQuery chosen dropdown experiences a visual issue when placed inside a scrollbar, appearing to be "cut

Good day, I've been using the jQuery "chosen" plugin for a dropdown menu, but I encountered an issue with it being located in a scrollable area. The problem is that the dropdown items are getting cut off by the outer div element. I have provided a si ...

Tips for sharing a link using req.flash() in express.js

Is there a way to pass a link in req.flash() within my JavaScript file, without involving it in the HTML template? I am using the alert message as a partial in other HTML pages and I want to include a "resend verification link". Do I need to create another ...

Ways to identify browser version in Angular 4 to discourage IE usage

Is there a method in Angular 4 (TypeScript) for detecting the browser type? I am currently working with Angular 4 and would like to explore options for identifying the browser type when my application is loaded. I specifically want to prevent my applicati ...

Exploring the functionality of NSPredicate with block implementations

I currently have a search feature in my application that utilizes an NSPredicate object to filter through objects. I'm a bit perplexed by the mysterious behavior occurring within my program's runtime. In my code, I have declared an array of name ...

Retrieve data from a MongoDB collection based on a specific index field

I am looking to extract only the "thetext" field from a specific index in my database when the value of "comment_count" is 1. This is the query I have tried: db.getCollection('mongotesi').find({},{'bug.long_desc.1.thetext':'1&apo ...

Damaged .xlsx document originating from a Flask web application and an ajax POST submission

I've encountered an issue with my Flask web app where a URL route receives a post request containing JSON, converts it to an .xlsx file, and returns it using send_file(). On the server side, the generated .xlsx file appears to be correct. However, wh ...

Transforming an array into key-value pairs where the keys are odd elements and the values are even elements

Is there a straightforward way to transform this initial array: [ "Bargain", "deal", "Consistent", "Steady; regular", "Accurately", "a thing bought or offered for sale much more cheaply than is usual or expected.", "Charge", "demand (an am ...

Ensuring the safety of props in JSX, similar to Jinja2 techniques

When utilizing getServerSideProps, I encounter a situation where the prop returned contains HTML tags and formatting. My goal is to have this content rendered as actual HTML rather than plain text. For example, if the prop returns <h1>This</h1& ...

Trouble with executing jquery window.open during ajax success due to blocking

Encountering an issue when trying to open a new browser window in my ajax success call, as it is being blocked as a popup. After some investigation, I discovered that a user event must be associated with the window.open method to prevent this from happenin ...

jquery click event triggers changes on several elements sharing a common class

I am looking to incorporate auto play functionality for a YouTube video on my video style page, which can be found at . Currently, I am able to trigger the video to auto play by clicking on an image thumbnail using the code below. However, I am encounterin ...

Transforming JSON data into a dynamic Tableview

I've been experimenting with this issue for quite some time now, but I can't seem to find a solution. My API returns tasks in JSON format. When I print the data using Ti.API.info(this.responseText), it looks like this: [INFO] [{"created_at":"20 ...

Acquire YouTube embeds from a Facebook community

Currently, I am in the process of extracting wall posts from a Facebook group feed. However, my main focus is on extracting the YouTube embed IDs only. After researching Facebook's Graph API, I have yet to find an easier method for directly extracting ...

Executing usemin and useminPrepare on various targets is made possible by Grunt

According to the usemin issues, it seems that the latest version of usemin and useminPrepare now support multiple targets: Updates related to multiple targets in useminPrepare: pull#162 pull#206 New features for usemin support: Multiple targets A ...

Updating MongoDB collections in Mongoose with varying numbers of fields: A step-by-step guide

Updating a mongodb collection can be challenging when you don't know which fields will be updated. For instance, if a user updates different pieces of information on various pages, the fields being updated may vary each time. Here is my current appro ...

Any suggestions on how to incorporate the variable into the inline JavaScript [[]] API path?

I have a query regarding creating a URL in JavaScript, but I'm unsure how to include variables within th:inline="javascript". Below is my code snippet: <script th:inline="javascript"> $(function() { $('#querySubmit').click(queryS ...

Ways to safeguard NextJS Route Handlers from unauthorized access beyond my website

While using the NextJS 14 App Router, I noticed that my Route Handlers for APIs were accessible to my friend via Postman from his PC. This was unexpected as I assumed they were protected by default due to the same-origin policy headers in NextJS. However, ...

What is the best way to hide a div when an image is positioned on top of it?

Previously, I inquired about creating a "cursor mirror" where the movement of the cursor in the top section of a website would be mirrored in the opposite direction in the bottom section. You can find the original question here. Expanding on that concept, ...

Can the values of an enum in TypeScript be altered dynamically?

Can the values of an enum be dynamically changed? I currently have an enum set up like this enum MyEnum{ Error = "Error", Warn = "Warnings" } I have realized that in order for the values like Error and Warnings to be translated int ...