skip to Main Content

With Javascript, I want to split an array and group by an attribute

input:

[
    {
        "type": "typeA",
        "label": "labelA",
        "placeholders": [
            "b",
            "a",
            "r"
        ]
    },{
        "type": "typeB",
        "label": "labelB",
        "placeholders": [
            "x",
            "y",
            "z"
        ]
    },{
        "type": "typeA",
        "label": "labelAAA",
        "placeholders": [
            "a",
            "b",
            "c"
        ]
    }
]

I want output:

[
  {
    "type": "typeA",
    "items": [
      {
        "label": "labelA",
        "placeholders": [
          "b",
          "a",
          "r"
        ]
      },
      {
        "label": "labelAAA",
        "placeholders": [
          "a",
          "b",
          "c"
        ]
      }
    ]
  },
  {
    "type": "typeB",
    "items": [
      {
        "label": "labelB",
        "placeholders": [
          "b",
          "a",
          "r"
        ]
      }
    ]
  }
]

I think javascript use .reduce, .concat, .map, …

I’d like to do this without additional libraries (e.g., lodash).

4

Answers


  1. Here is an example I created in JSFiddle that should get you the result you’re looking for.

    https://jsfiddle.net/9kman64x/

    const input = [  
      {  
        "type": "typeA",  
        "label": "labelA",  
        "placeholders": ["b", "a", "r"]  
      },  
      {  
        "type": "typeB",  
        "label": "labelB",  
        "placeholders": ["x", "y", "z"]  
      },  
      {  
        "type": "typeA",  
        "label": "labelAAA",  
        "placeholders": ["a", "b", "c"]  
      }  
    ];  
     //You can See example here
     //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
    const output = input.reduce((acc, item) => {  
      // Find the existing group for the current item's type  
      const group = acc.find(g => g.type === item.type);  
      
      // If the group exists, add the item to it  
      if (group) {  
        group.items.push({  
          label: item.label,  
          placeholders: item.placeholders  
        });  
      } else {  
        // If the group doesn't exist, create a new one and add it  
        acc.push({  
          type: item.type,  
          items: [  
            {  
              label: item.label,  
              placeholders: item.placeholders  
            }  
          ]  
        });  
      }  
      return acc;  
    }, []);  
      
    console.log(output); 
    

    It uses the reduce method on the array and either adds it to a known group or makes a new one.

    Login or Signup to reply.
  2. As I said in comments, since you’re grouping by type – making it basically unique per se, you would be better going for an Object as response (instead of array). It’s easier to do so, and easier to retrieve data from:

    const arr = [
      {"type": "typeA","label": "labelA","placeholders": ["b","a","r"]},
      {"type": "typeB","label": "labelB","placeholders": ["x","y","z"]},
      {"type": "typeA","label": "labelAAA","placeholders": ["a","b","c"]}
    ];
    
    const groups = arr.reduce((acc, ob) => {
      acc[ob.type] ??= {type: ob.type, items: []};
      acc[ob.type].items.push(ob);
      return acc
    }, {});
    
    console.log(groups)

    then, to get the length of your groups use:

    const groupsTot = Object.keys(groups).length;
    

    or to get an Array (as requested, but which I think is not necessary), use:

    const groupsArray = [Object.values(groups)];
    
    Login or Signup to reply.
  3. One of the best ways to approach this is to reduce the array into a Map and group by type.

    Calling Map.prototype.values() returns an iterator, so you will need to convert to an array via:

    • Array.from(iterator) or
    • [...iterator] (spread oprator)
    const input = [{
      "type": "typeA",
      "label": "labelA",
      "placeholders": ["b", "a", "r"]
    }, {
      "type": "typeB",
      "label": "labelB",
      "placeholders": ["x", "y", "z"]
    }, {
      "type": "typeA",
      "label": "labelAAA",
      "placeholders": ["a", "b", "c"]
    }];
    
    const output = Array.from(
      input.reduce((typeMap, { type, ...rest }) => {
        if (!typeMap.has(type)) {
          typeMap.set(type, { type, items: [ { ...rest } ] });
        } else {
          typeMap.get(type).items.push({ ...rest });
        }
        return typeMap;
      }, new Map).values());
    
    console.log(output);
    .as-console-wrapper { top: 0; max-height: 100% !important; }
    Login or Signup to reply.
  4. You could do something like this:

    const groupedArray = originalArray.reduce(([acc, curr]) => {
      const { type: currentType, ...rest } = curr;
    
      // Check if the type exists in the grouping
      const indexOfCurrentType = acc.findIndex(({ type }) => type === currentType);
      
      // This grouping already exists. Let's add to the items array
      if (indexOfCurrentType > -1) {
        acc[indexOfCurrentType].items.push(rest);
      } else {
        acc.push({
          type: currentType,
          items: [rest],
        })
      }
    
      return acc;
    }, []);
    

    The idea is to incrementally build the new array, checking if we have already seen a certain type. If we have, add it to the list of items; otherwise, create a new entry.
    It’s not the most optimal solution because we have to loop through every type to see if we have a match on each iteration.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search