unseen_damage
unseen_damage

Reputation: 1376

Group a Javascript Array

I am trying to take data from one array, group the data by object property, and then send it to a new array. The current data format is as such:

const data = [
    {
        "dataId": "1",
        "tableName": "table1",
        "column": "firstHeader",
        "rows": [
            "a","b","c"
        ]
    },
    {
        "dataId": "2",
        "tableName": "table1",
        "column": "secondHeader",
        "rows": [
            "d","e","f",
        ]
    },
    {
        "dataId": "3",
        "tableName": "table2",
        "column": "aNewFirstHeader",
        "rows": [
            1,2,3
        ]
    }
];

I used the Lodash groupby method, which groups the items in a format that is close to what I am expecting, but not exactly what I want my end result to be. The lodash result is:

[
    [
        { "tableName": 'table1',
            "dataId": '1',
            "header": 'firstHeader',
            "rows": ["a","b","c"]
        },

        { "tableName": 'table1',
            "dataId": '1',
            "header": 'secondHeader',
            "rows": ["d","e","f"]
        }
    ],

    [
        { "tableName": 'table2',
            "dataId": '2',
            "header": 'aNewFirstHeader',
            "rows": [1,2,3] },
    ]
]

Ultimately, what I am looking for is the end result of this:

[
    {
    "tableName": "table1",
    "column": ["firstHeader", "secondHeader"],
    "rows": [["a","b","c"], ["d","e","f"]]
    },
    {
    "tableName": "table2",
    "column": ["aNewFirstHeader"],
    "rows": [[1,2,3]]
    }
]

Is there a different way to use the groupBy method, or perhaps a javascript only approach that would get the end result I am looking for?

Upvotes: 2

Views: 923

Answers (9)

Intricus
Intricus

Reputation: 1

Using ES6 with unique key approach:

"Way of the future" :-)

http://es6-features.org/

const desiredResults = [{
      "dataId": "1",
      "tableName": "table1",
      "column": "firstHeader",
      "rows": [
        "a", "b", "c"
      ]
    },
    {
      "dataId": "2",
      "tableName": "table1",
      "column": "secondHeader",
      "rows": [
        "d", "e", "f",
      ]
    },
    {
      "dataId": "3",
      "tableName": "table2",
      "column": "aNewFirstHeader",
      "rows": [
        1, 2, 3
      ]
    }
  ]
  .reduce((unqiueMap, {
    dataId, // Destructuring each data element
    tableName,
    column,
    rows
  }) => {
    if (tableName in unqiueMap) {
      unqiueMap[tableName].column.push(column);
      unqiueMap[tableName].rows.push(rows);
    } else {
      unqiueMap[tableName] = {
        column: [column],
        rows: [rows]
      }
    }
    return unqiueMap;
  }, {})

console.log(desiredResults)

Upvotes: 0

Kahlil Lechelt
Kahlil Lechelt

Reputation: 566

Edit: Oh, I totally forgot that .findIndex() is a thing. Here is the updated solution:

Use .map(), .reduce() & .findIndex():

const newArr = data.map(value => ({
        tableName: value.tableName,
        column: [value.column],
        rows: [value.rows],
    }))
    .reduce((acc, cur) => {
        const tableNameIndex = acc.findIndex(el => el.tableName === cur.tableName);
        if (tableNameIndex !== -1) {
            acc[tableNameIndex].column.push(cur.column[0]);
            acc[tableNameIndex].rows.push(cur.rows[0]);
            return acc;
        } else {
            acc.push(cur);
            return acc;
        }
    }, [])

console.log(newArr);

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386560

In plain Javascript, you could use Map

var data = [{ dataId: "1", tableName: "table1", column: "firstHeader", rows: ["a", "b", "c"] }, { dataId: "2", tableName: "table1", column: "secondHeader", rows: ["d", "e", "f", ] }, { dataId: "3", tableName: "table2", column: "aNewFirstHeader", rows: [1, 2, 3] }],
    result = data.reduce((map => (r, a) => {
        var item = map.get(a.tableName);
        if (!item) {
            item = { tableName: a.tableName, column: [], rows: [] }
            map.set(a.tableName, item);
            r.push(item);
        }
        item.column.push(a.column);
        item.rows.push(a.rows);
        return r;
    })(new Map), []);

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

Upvotes: 0

Amnon
Amnon

Reputation: 2888

Plain Javascript:

function groupBy(data, fieldName) {
    var groups = {}
    for (var i=0; i<data.length; i++) {
        var elem = data[i];
        if (fieldName in elem) {
            var fieldVal = elem[fieldName];
            if (!(fieldVal in groups)) {
                groups[fieldVal] = [];
            }
            groups[fieldVal].push(elem)
        }
    }
    var result = [];
    for (list in groups) {
        var arr = groups[list];
        var combine = {}
        combine[fieldName] = arr[0][fieldName];
        for (var i=0; i<arr.length; i++) {
            var elem = arr[i];
            for (prop in elem) {
                if (prop == fieldName) {
                    continue;
                }
                if (!(prop in combine)) {
                    combine[prop] = [];
                }
                combine[prop].push(elem[prop]);
            }
        }
        result.push(combine);
    }
    return result;
}

For your case, simply invoke:

groupBy(data, "tableName")

Upvotes: 0

Ori Drori
Ori Drori

Reputation: 191976

After using _.groupBy() map each group using _.mergeWith() to combine all objects in the group to a new object, and push everything, except tableName into an array. Then use _.omit() to remove dataId:

const data = [{"dataId":"1","tableName":"table1","column":"firstHeader","rows":["a","b","c"]},{"dataId":"2","tableName":"table1","column":"secondHeader","rows":["d","e","f"]},{"dataId":"3","tableName":"table2","column":"aNewFirstHeader","rows":[1,2,3]}];

const result = _(data)
  .groupBy('tableName')
  .map((group) => // map each group
       // remove the dataId property
    _.omit( 
      // merge all group objects
      _.mergeWith({}, ...group, (objValue = [], srcValue, key) => {
        // if the key is not tableName push it into the array
        if (key !== 'tableName') {
          objValue.push(srcValue);
          return objValue;
        }
      }),
      'dataId'))
  .value();

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Upvotes: 0

p0larBoy
p0larBoy

Reputation: 1310

By using reduce and findIndex, we can rebuild a new array which suits your format in O(n) time.

data.reduce((arr, record) => {
  const index = arr.findIndex((inside) => (inside.tableName === record.tableName));

  if (index === -1) { 
    arr.push({
        tableName: record.tableName,
        column: [record.column],
        rows: [record.rows]
    }) 
  } else {
    arr[index].column.push(record.column);
    arr[index].rows.push(record.rows);
  }

  return arr;
}, []);

const data = [
{
    "dataId": "1",
    "tableName": "table1",
    "column": "firstHeader",
    "rows": [
        "a", "b", "c"
    ]
},
{
    "dataId": "2",
    "tableName": "table1",
    "column": "secondHeader",
    "rows": [
        "d", "e", "f",
    ]
},
{
    "dataId": "3",
    "tableName": "table2",
    "column": "aNewFirstHeader",
    "rows": [
        1, 2, 3
    ]
}
];

const newData = data.reduce((arr, record) => {
  const index = arr.findIndex((inside) => (inside.tableName === record.tableName));

  if (index === -1) { 
	arr.push({
        tableName: record.tableName,
		column: [record.column],
		rows: [record.rows]
	}) 
  } else {
	arr[index].column.push(record.column);
    arr[index].rows.push(record.rows);
  }
  return arr;

}, []);

console.log(newData);

Upvotes: 2

abc123
abc123

Reputation: 18763

jsFiddle

const data = [
    {
        "dataId": "1",
        "tableName": "table1",
        "column": "firstHeader",
        "rows": [
            "a","b","c"
        ]
    },
    {
        "dataId": "2",
        "tableName": "table1",
        "column": "secondHeader",
        "rows": [
            "d","e","f",
        ]
    },
    {
        "dataId": "3",
        "tableName": "table2",
        "column": "aNewFirstHeader",
        "rows": [
            1,2,3
        ]
    }
];

var result = _.chain(data)
    .groupBy("tableName")
    .pairs()
    .map(function (currentItem) {
        // used to create desired properties
        var rows = [];
        var columns = [];
        // loop to fill values
        for(var i=0,length=currentItem[1].length;i<length;i++){
          columns.push(currentItem[1][i].column)
          rows = rows.concat(currentItem[1][i].rows);
        }
        return {
          'tableName': currentItem[0],
          'column': columns,
          'rows': rows
        };
    })
    .value();
console.log(result);

Upvotes: 0

dev8080
dev8080

Reputation: 4020

Try this , used plain vanilla js only:

var merge = function( data){
        var outputArray = [], enteredNames = [];
        for(var i=0; i<data.length; i++){
            var datum = data[i];
            var ownElement;
            if(enteredNames.indexOf(datum.tableName)>-1)
                ownElement = outputArray[enteredNames.indexOf(datum.tableName)];
            else
                {
                ownElement = {

                    "tableName": datum.tableName,
                    "column": [],
                    "rows": []

                    }
                enteredNames.push( datum.tableName ); 
                outputArray.push( ownElement );
                }

            ownElement.column.push( datum.column );
            ownElement.rows.push( datum.rows );

        }
        return outputArray;
}

Upvotes: 0

Cameron
Cameron

Reputation: 1524

Use the built-in JavaScript Array Prototype methods map and reduce to simplify your problem:

const data = [
    {
        "dataId": "1",
        "tableName": "table1",
        "column": "firstHeader",
        "rows": [
            "a", "b", "c"
        ]
    },
    {
        "dataId": "2",
        "tableName": "table1",
        "column": "secondHeader",
        "rows": [
            "d", "e", "f",
        ]
    },
    {
        "dataId": "3",
        "tableName": "table2",
        "column": "aNewFirstHeader",
        "rows": [
            1, 2, 3
        ]
    }
];

const EXPECTED = [
    {
        "tableName": "table1",
        "column": ["firstHeader", "secondHeader"],
        "rows": [["a", "b", "c"], ["d", "e", "f"]]
    },
    {
        "tableName": "table2",
        "column": ["aNewFirstHeader"],
        "rows": [[1, 2, 3]]
    }
];

var result = data
    .reduce((hash, dataItem) => {
        // first group by tableName
        if (hash[dataItem.tableName]) {
            hash[dataItem.tableName].column.push(dataItem.column);
            hash[dataItem.tableName].rows.push(dataItem.rows);
        } else {
            hash[dataItem.tableName] = {
                column: [dataItem.column],
                rows: [dataItem.rows]
            };
        }

        return hash;
    }, {});

// run a map to change to the exact array style you want
result = Object.keys(result).map((tableName) => {
    return {
        tableName: tableName,
        column: result[tableName].column,
        rows: result[tableName].rows
    };
});

// if in a node environment, you can assert (for unit testing only)
require('assert').deepStrictEqual(result, EXPECTED);

Upvotes: 0

Related Questions