Is there a way to generate a fresh array by filtering an array of objects based on a single value?

I am currently working with an array of objects:

const dummyLinkRows = [
  {
    id: 'entity:link/1:en',
    categories: [
      {
        name: 'Human Resources'
      },
      {
        name: 'Social'
      }
    ],
    name: 'Facebook',
    url: 'https://www.facebook.com'
  },
  {
    id: 'entity:link/2:en',
    categories: [
      {
        name: 'Human Resources'
      }
    ],
    name: 'Other HR',
    url: 'https://www.hr.com'
  },
  {
    id: 'entity:link/3:en',
    categories: [
      {
        name: 'Zen Mode'
      }
    ],
    name: 'Zebra',
    url: 'https://www.zebra.com'
  },
  {
    id: 'entity:link/4:en',
    categories: [
      {
        name: 'Social'
      }
    ],
    name: 'Zebra',
    url: 'https://www.instagram.com'
  },
];

I am looking to group these links/objects by category for rendering purposes.

The desired structure of the new array should be as follows:

export const NEWDummyLinkRows = [
  {
    category: { name: 'Social' },
    links: [
      {
        name: 'Facebook',
        url: 'https://www.facebook.com'
      },
      {
        name: 'Instagram',
        url: 'https://www.instagram.com'
      }
    ]
  },
  {
    category: { name: 'Human Resources' },
    links: [
      {
        name: 'Other HR',
        url: 'https://www.hr.com'
      },
      {
        name: 'Zebra HR',
        url: 'https://www.zebra.com'
      }
    ]
  },  
];

In my React render method, I have implemented the following code snippet:

        {props.rows &&
          props.rows.map((row, index) => {
           return (
            <div key={index}>
              <h4>{get(row, 'categories')[index].name}</h4>
              <ul className='link-group--list'>
                {row.categories.map((link, index) => {
                  return (
                    <li key={index}>
                      <a href={row.url}>
                        {link.name}
                      </a>
                    </li>
                  );
                })}
              </ul>
            </div>
          );
        })}

Although the current implementation renders data, it does not produce the expected output. I am seeking a solution using pure ES6/JavaScript methods.

Answer №1

To achieve the desired outcome, one approach is to first simplify the data into an object and then transform the object's entries according to your specified format:

const result = Object.entries(rows.reduce((a, {name, url, categories}) => {
  categories.forEach(c => {
    a[c.name] = a[c.name] || [];
    a[c.name].push({name, url});
  });

  return a;
}, {})).map(([name, links]) => ({ category: {name}, links }));

Complete code snippet:

const rows = [{
    id: 'entity:link/1:en',
    categories: [{
        name: 'Human Resources'
      },
      {
        name: 'Social'
      }
    ],
    name: 'Facebook',
    url: 'https://www.facebook.com'
  },
  {
    id: 'entity:link/2:en',
    categories: [{
      name: 'Human Resources'
    }],
    name: 'Other HR',
    url: 'https://www.hr.com'
  },
  {
    id: 'entity:link/3:en',
    categories: [{
      name: 'Zen Mode'
    }],
    name: 'Zebra',
    url: 'https://www.zebra.com'
  },
  {
    id: 'entity:link/4:en',
    categories: [{
      name: 'Social'
    }],
    name: 'Zebra',
    url: 'https://www.instagram.com'
  }
];

const result = Object.entries(rows.reduce((a, {name, url, categories}) => {
  categories.forEach(c => {
    a[c.name] = a[c.name] || [];
    a[c.name].push({name, url});
  });
  
  return a;
}, {})).map(([name, links]) => ({ category: {name}, links }));

console.log(result);

Answer №2

One efficient way is to utilize the reduce function along with Map

const dummyLinkRows = [{id: 'entity:link/1:en',categories: [{name: 'Human Resources'},{name: 'Social'}],name: 'Facebook',url: 'https://www.facebook.com'},{id: 'entity:link/2:en',categories: [{name: 'Human Resources'
}],name: 'Other HR',url: 'https://www.hr.com'},{id: 'entity:link/3:en',categories: [{name: 'Zen Mode'}],name: 'Zebra',url: 'https://www.zebra.com'},{id: 'entity:link/4:en',categories: [{name: 'Social'}],name: 'Zebra',url: 'https://www.instagram.com'}];

const final = dummyLinkRows.reduce((op,inp) => {
  let {name: nameOuter, categories, url} = inp
  categories.forEach(({name}) => {
    if(op.has(name)){
      op.get(name).links.push({name: nameOuter, url})
    } else{
      op.set(name, {catgeory:{name}, links:[{name:nameOuter, url}] })
    }
  })
  return op
},new Map())

console.log([...final.values()])

Answer №3

To achieve this in a more natural way, you can follow these steps:

const data = [
  {
    id: 'entity:link/1:en',
    categories: [
      {
        name: 'Technology',
      },
      {
        name: 'Software',
      },
    ],
    title: 'Google',
    link: 'https://www.google.com',
  },
  {
    id: 'entity:link/2:en',
    categories: [
      {
        name: 'Technology',
      },
    ],
    title: 'Microsoft',
    link: 'https://www.microsoft.com',
  },
  {
    id: 'entity:link/3:en',
    categories: [
      {
        name: 'Healthcare',
      },
    ],
    title: 'Mayo Clinic',
    link: 'https://www.mayoclinic.org',
  },
];
const filteredData = [];
for (let i = 0; i < data.length; i += 1) {
  const item = data[i];
  const { categories } = item;
  if (categories && Array.isArray(categories)) {
    for (let j = 0; j < categories.length; j += 1) {
      const category = categories[j];
      const index = filteredData.findIndex((r) => { return r.category && r.category.name === category.name; });
      if (index === -1) {
        const obj = {
          category: { name: category.name },
          links: [{ title: item.title, link: item.link }],
        };
        filteredData.push(obj);
      } else {
        const obj = { title: item.title, link: item.link };
        filteredData[index].links.push(obj);
      }
    }
  }
}
console.log(filteredData); 

Answer №4

I believe utilizing the reduce method is the most effective approach in this scenario. By invoking .reduce on an array and providing it with a function and an initial object, you can achieve the desired outcome. The function receives 'previous' (the previous value) and 'current' (the current array value) as parameters and returns the result. This result serves as the 'previous' for the subsequent function calls and as the final output after the last call.

Let's take one of the elements from your list:

  {
    id: 'entity:link/1:en',
    categories: [
      {
        name: 'Human Resources'
      },
      {
        name: 'Social'
      }
    ],
    name: 'Facebook',
    url: 'https://www.facebook.com'
  },
... (remaining text remains unchanged)    

Answer №5

Attempt (h={})

dummyLinkRows.forEach(x=> x.categories.forEach(c=> 
  h[c.name] = (h[c.name]||[]).concat([{name:x.name, url:x.url}]) ))

let NEWDummyLinkRows = Object.keys(h).map(k=> ({category:k, links: h[k]}) )

const dummyLinkRows = [
  {
    id: 'entity:link/1:en',
    categories: [
      {
        name: 'Human Resources'
      },
      {
        name: 'Social'
      }
    ],
    name: 'Facebook',
    url: 'https://www.facebook.com'
  },
  {
    id: 'entity:link/2:en',
    categories: [
      {
        name: 'Human Resources'
      }
    ],
    name: 'Other HR',
    url: 'https://www.hr.com'
  },
  {
    id: 'entity:link/3:en',
    categories: [
      {
        name: 'Zen Mode'
      }
    ],
    name: 'Zebra',
    url: 'https://www.zebra.com'
  },
  {
    id: 'entity:link/4:en',
    categories: [
      {
        name: 'Social'
      }
    ],
    name: 'Zebra',
    url: 'https://www.instagram.com'
  },
];

let h={};

dummyLinkRows.forEach(x=> x.categories.forEach(c=> 
  h[c.name]=(h[c.name]||[]).concat([{name:x.name, url:x.url}]) ) )

let NEWDummyLinkRows = Object.keys(h).map(k=> ({category:k, links: h[k]}) )

console.log(NEWDummyLinkRows);

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

What is the best way to deduct a variable's previous value from the final value, ensuring that the total value does not surpass a specific limit?

For instance: let num = 20; const sub = 6; const add = 10; num = num - sub; num = num + add; if (num > 20){ num = 20; } console.log("Only 6 was actually added to var num before reaching its maximum value"); Is there a way to adjust the console log ...

Can you explain the concept of cross domain and how JSONP fits into the picture?

As a beginner in .net programming, I have created a webservice where JavaScript calls the webservice in my code. I attempted to call it using my phone's browser while on the same network. It works perfectly with localhost, but when trying to call the ...

Using Vue's v-bind directive with a single set of curly braces expression

Currently, I am delving into the world of Vue.js to broaden my knowledge and gain practical experience. While following a tutorial, I encountered an interesting scenario involving the addition of a class to a span element based on a condition within a v-f ...

Ensuring the dropdown menu's functionality in AngularJS

I'm currently working on a code snippet that validates when the user moves away from a specific select element. The goal is to change the border color of the select element to either red or green only when the user tabs away from it or moves the mouse ...

Troubleshooting: Issue with incorporating libraries into my HTML code using Ionic framework and Angular

I am currently working on developing a hybrid app using Ionic and Angular. I attempted to incorporate jQuery UI for drag-and-drop functionality, but unfortunately, it did not work as expected. Despite testing simple examples, the feature still did not func ...

Is there a method to determine if localForage/indexedDb is currently handling data operations?

Currently, I am working on a webapp that utilizes async localForage (specifically angular localForage). My goal is to alert users if they attempt to close the browser window while there are ongoing localForage operations. I have observed that some browser ...

Step by step guide on inserting a message (memo/sticky note) onto an HTML page

Wondering how to accomplish this: I've designed an HTML5 homepage and I'm looking to display a message to visitors, such as "Dear visitor, I am currently on vacation from....", in the form of a memo or sticky note positioned in the top right cor ...

A guide to creating a synchronous AJAX call using vanilla JavaScript

I am trying to make multiple AJAX calls in a loop using the code below. for (var i = 0; i < 5; i++) { console.log(i); ajax_DatabaseAccessor.query("CheckInt", i, loadQuery); function loadQuery(data) { alert(DWRUtil.toDescriptiveString ...

Storing user data in node.js using the express-sessionTo save user data in

Using express and express-session with mysql on nodeJS has been successful for me. I managed to set up a cookie and session as well. Take a look at my code: app.use(cookieParser('3CCC4ACD-6ED1-4844-9217-82131BDCB239')); session({resave: true, s ...

Incorporating a delete button onto an image using JavaScript

I am currently developing a BlogApp and facing difficulty in attempting to include a button on an image. The image is being retrieved from the Django database in JavaScript (HTML). My goal is to have a clickable button overlaid on the image. views.py def ...

How to retrieve user data based on ID using Angular SDK

I have limited experience with the Angular SDK and lb-service, and I'm unsure about how to retrieve another user's information by their ID within a controller. I am trying to implement a feature for displaying a friend list, where each user only ...

Is it possible to pass a parameter to an NGXS action that is not the Payload?

I am working on implementing an Ngxs action that includes a parameter in addition to the payload. This parameter is used to determine whether or not a notification should be sent. @Action(UpdateUser) async UpdateUser( ctx: StateContext<ProfileStat ...

Revamp your arrays with input fields in Vue3

When presented with two arrays of options, users must select an option from each array. ==> first array: [ orange, green, yellow ] ==> second array: [ orange, green, yellow ] The challenge is to update the second array based on the user's sele ...

Text displaying as actionable icons

In my React project, I am utilizing material-table (https://material-table.com/#/) and have successfully imported the necessary icons. However, when viewing the action bar, the icons are displaying as plain text instead of the Material Icon. import React, ...

What sets apart the `ajax:send` event from the `click` event is that it specifically triggers an ajax

Currently, I am utilizing ajax requests with coffee in my Rails project as shown below. $('#reload').on( 'click': -> $('#response').html "<div class='panel'><img src='assets/load.gif'/> ...

"Endowed with improper dimensions, the BootStrap collapse feature

Yesterday, I posted about an issue with BootStrap and panel collapsables causing graph sizes to become distorted. The post was locked because there was no accompanying code. I have now created a code snippet for you all to see the exact problem I am facing ...

Engaging 3D Object with JavaScript Interactivity

I'm currently working on a project for my HCI assignment where I need to create an interactive Sphere using JavaScript. However, I am new to JavaScript and Three.js. My goal is to have the sphere display statistics of a specific subject when clicked. ...

Stop GIFs from playing automatically

Looking for a solution to disable autoplay for animated gifs on my chat-site (php-based). Tried script below but out of ideas: <script> myVid=document.getElementsByTagName('img'); function disableAutoplay() { myVid.autoplay=false; m ...

Repeating every 3 to 6 months on later.js commencing from a specified date

Currently, I am working on setting up recurring events every 3 and 6 months using the later.js library which can be found at https://github.com/bunkat/later. The code implementation looks like this: // Assuming my value.scheduled_date is set to 2018-09-0 ...

When attempting to upload multiple images to my backend, I am consistently receiving an empty array as a result

I have developed an API on the backend that is designed to store images in the database. I have implemented a function for uploading these images, but unfortunately, I am encountering an issue where my database receives an empty array. Interestingly, when ...