简体   繁体   中英

How can I create a new array from an array of objects filter by one value?

So I have this 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'
  },
];

Basically what I need to do is to group the links/objects by category so I may render them which the correspondent category title.

I will need a new array to be like this:

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'
      }
    ]
  },  
];

Here is what I have so far in a react render method:

        {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>
          );
        })}

So far it renders data but not as I need it. The method I need could be using pure ES6/JavaScript.

A solution could be to first reduce to an object, and then map the object's entries to your expected result:

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 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); 

You can use reduce and 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()]) 

I think the best way is to use reduce . You call .reduce on an array, pass it a function an a starting object. The function takes parameters 'previous' (the previous value) and 'current' (the current array value) and returns the result, which will be 'previous' for the next call to the function and the final result after the last call.

So take one of your elements:

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

You seem to want an array of objects with a category object property and a list of objects with a property of category which is an object with a name and a list of links with name and url. I think the resulting objects should be categories themselves with just a name, but whatever. As a first step you need to make categories unique. A javascript object is perfect for that, with the the property names being your keys. So this sample would end up being converted to:

{
  "Human Resources": {
    links: [
      { name: 'Facebook', url: 'https://www.facebook.com' }
    ]
  },
  "Social": {
    links: [
      { name: 'Facebook', url: 'https://www.facebook.com' }
    ]
  }      
}

So we can start with an empty object. This will have a property with the name of each category, and the value of that property will be an object with a 'links' array of objects with the name and url of the link. So for each element in your original array we create a property if it doesn't exist like {links: []} If the object already exists, we use it. Then we add the new link to the 'links' array.

function reducer(previous, current) {
  // previous will start as empty object and become our result, current
  // is the current element of the array we are processing

  // loop through each category
  current.categories.forEach(category => {
    // create a new object with links array if the category doesn't exist
    previous[category.name] = previous[category.name] || { links: [] };

    // add link to category
    previous[category.name].links.push({ name: current.name, url: current.url });
  });

  // return 'previous' which is our ongoing object we're creating
  return previous;
}

// call reducer for each element of our array, starting with an empty object
let intermediate = dummyLinkRows.reduce(reducer, {});

Now you have:

intermediate = {
  "Human Resources": {
    links: [
      { name: 'Facebook', url: 'https://www.facebook.com' }
    ]
  },
  "Social": {
    links: [
      { name: 'Facebook', url: 'https://www.facebook.com' }
    ]
  }      
}

So we've created a unique object for each category. To get your final array, you want an item for each category with a 'category' property that is an object with the name and our links array. So we can use Object.keys to get the list of category names (property names of the object). Using map() to convert those keys to the objects you want:

let final = Object.keys(intermediate).map(key => {
  return {
    category: { name: key },
    links: intermediate[key];
  };
});

In a native way, you can simply achieve it like this

 const array = [ { 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 filteredArry = []; for (let i = 0; i < array.length; i += 1) { const element = array[i]; const { categories } = element; if (categories && Array.isArray(categories)) { for (let j = 0; j < categories.length; j += 1) { const cats = categories[j]; const index = filteredArry.findIndex((r) => { return r.catgeory && r.catgeory.name === cats.name; }); if (index === -1) { const obj = { catgeory: { name: cats.name }, links: [{ name: element.name, url: element.url }], }; filteredArry.push(obj); } else { const obj = { name: element.name, url: element.url }; filteredArry[index].links.push(obj); } } } } console.log(filteredArry); 

Try (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); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM