How to merge values within array of objects

I have an array with the following values:

let orders = [
{n: 0, orderId: null, lat: 49.396315, lng: 15.609607, type: "start"}, 
{n: 2.5, orderId: 26889, lat: 48.98951, lng: 14.45937, type: "odes"}, 
{n: 9, orderId: 26969, lat: 50.0823536, lng: 12.3791268, type: "odes"}, 
{n: 9, orderId: 26995, lat: 50.0823536, lng: 12.3791268, type: "odes"}, 
{n: 31, orderId: null, lat: 50.7343366, lng: 15.0501002, type: "end"}, 
{n: 31, orderId: 26969, lat: 50.7343366, lng: 15.0501002, type: "prij"}, 
{n: 31, orderId: 26995, lat: 50.7343366, lng: 15.0501002, type: "prij"},
];

and I would like to have following result:

[
{n: 0, orderId: null, lat: 49.396315, lng: 15.609607, type: "start"}, 
{n: 2.5, orderId: 26889, lat: 48.98951, lng: 14.45937, type: "odes"}, 
{n: 9, orderId: [26969,26995], lat: 50.0823536, lng: 12.3791268, type: ["odes", "odes"]}, 
{n: 31, orderId: [null,26969,26995], lat: 50.7343366, lng: 15.0501002, type: ["end", "prij", "prij"]}, 
]

Basically, I want to merge values if n, lat and long are the same, any ideas on how to elegantly do this?

Thanks in advance

Upvotes: 1

Views: 67

Answers (4)

Mister Jojo
Mister Jojo

Reputation: 22361

Array reduce can do that:

let orders = 
    [ { n:  0,   orderId:  null, lat: 49.396315,  lng: 15.609607,  type: 'start' } 
    , { n:  2.5, orderId: 26889, lat: 48.98951,   lng: 14.45937,   type: 'odes'  } 
    , { n:  9,   orderId: 26969, lat: 50.0823536, lng: 12.3791268, type: 'odes'  } 
    , { n:  9,   orderId: 26995, lat: 50.0823536, lng: 12.3791268, type: 'odes'  } 
    , { n: 31,   orderId:  null, lat: 50.7343366, lng: 15.0501002, type: 'end'   } 
    , { n: 31,   orderId: 26969, lat: 50.7343366, lng: 15.0501002, type: 'prij'  } 
    , { n: 31,   orderId: 26995, lat: 50.7343366, lng: 15.0501002, type: 'prij'  } 
    ] 
    
let res = orders.reduce((a,c,i,t)=>
  {
  if ( i                                // is equivalent to  test (i> 0)
    && c.n   === t[i-1].n
    && c.lat === t[i-1].lat
    && c.lgn === t[i-1].lgn )
    {
    let el = a[a.length-1]              // last accumulator element
    if (!Array.isArray( el.orderId ))  
      {                                 // change elements to array type
      el.orderId = [ el.orderId ]
      el.type    = [ el.type ]
      }
    el.orderId.push(c.orderId)         // add new values on
    el.type.push(c.type)
    }
  else
    a.push({...c})   // this one is the first with the keys combinations
  return a
  }
  ,[])  // answer initialisation with an empty array
 
console.log( res )
.as-console-wrapper { max-height: 100% !important; top: 0; }

If you don't want to create a new array, but just "cleaning" your original one, do:

let orders = 
    [ { n:  0,   orderId:  null, lat: 49.396315,  lng: 15.609607,  type: 'start' } 
    , { n:  2.5, orderId: 26889, lat: 48.98951,   lng: 14.45937,   type: 'odes'  } 
    , { n:  9,   orderId: 26969, lat: 50.0823536, lng: 12.3791268, type: 'odes'  } 
    , { n:  9,   orderId: 26995, lat: 50.0823536, lng: 12.3791268, type: 'odes'  } 
    , { n: 31,   orderId:  null, lat: 50.7343366, lng: 15.0501002, type: 'end'   } 
    , { n: 31,   orderId: 26969, lat: 50.7343366, lng: 15.0501002, type: 'prij'  } 
    , { n: 31,   orderId: 26995, lat: 50.7343366, lng: 15.0501002, type: 'prij'  } 
    ] 

for (let i=orders.length; (--i>0); )  // for i = orders.length-1 to 1 
  {
  let el_c = orders[i]      // pointers on current 
    , el_p = orders[i-1]   // and previous array elements
    ;
  if ( el_c.n   === el_p.n
    && el_c.lat === el_p.lat
    && el_c.lng === el_p.lng )    // if same keys
    {
    el_p.orderId = [ el_p.orderId, el_c.orderId ].flat() 
    el_p.type    = [ el_p.type,    el_c.type    ].flat()
    orders.splice(i,1)    // remove duplicate
  } } 
  
console.log( orders )
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Shashank Garg
Shashank Garg

Reputation: 296

let orders = [
  { n: 0, orderId: null, lat: 49.396315, lng: 15.609607, type: "start" },
  { n: 2.5, orderId: 26889, lat: 48.98951, lng: 14.45937, type: "odes" },
  { n: 9, orderId: 26969, lat: 50.0823536, lng: 12.3791268, type: "odes" },
  { n: 9, orderId: 26995, lat: 50.0823536, lng: 12.3791268, type: "odes" },
  { n: 31, orderId: null, lat: 50.7343366, lng: 15.0501002, type: "end" },
  { n: 31, orderId: 26969, lat: 50.7343366, lng: 15.0501002, type: "prij" },
  { n: 31, orderId: 26995, lat: 50.7343366, lng: 15.0501002, type: "prij" }
];

let map = new Map();

orders.forEach((record) => {
  let key = `n: ${record.n}, lat: ${record.lat}, lng: ${record.lng}`;

  if (map.has(key)) {
    let data = map.get(key);
    data.push(record);
    map.set(key, data);
  } else {
    map.set(key, [record]);
  }
});

const nOrders = [];

map.forEach((value, key) => {
  nOrders.push({
    n: value[0].n,
    lat: value[0].lat,
    lng: value[0].lng,
    orderId:
      value.length === 1
        ? value[0].orderId
        : value.map((entry) => entry.orderId),
    type: value.length === 1 ? value[0].type : value.map((entry) => entry.type)
  });
});

console.log(nOrders);

Upvotes: 1

farvilain
farvilain

Reputation: 2562

A simple naive solution consist of using a combined key, like ${n}-${lat}-${lng}. I added '-' separator as it seems to be absent of n/lat/lng.

const results = {};

orders.forEach( elem => {
    const key = `${elem.n}-${elem.lat}-${elem.lng}`;

    //Already present, just add type and orderId
    //Values can be duplicate, but you don't talk about that
    if ( result[key] ) {
        results[key].type.push( elem.type );
        results[key].orderId.push( elem.orderId );
    } else {
        results[key] = {
            n: elem.n,
            orderId: [elem.orderId],
            lat: elem.lat,
            lng: elem.lng,
            type: [elem.type]
        };
    }
});

Using the .reduce method of Array could be better, you should give a look.

Upvotes: 1

Terry
Terry

Reputation: 66218

You can take advantage of Array.prototype.reduce, but some tweaking of the internal logic is required since the grouped properties can be string/number or an array thereof.

In the reduce callback, you can attempt to search for an existing entry by the grouping criteria, which you say it should be n, lat and lng. If an entry is not found, you simply push the current item into your accumulator.

If an entry is found, then you need to perform some logic to convert the pre-existing string/number value into an array, and then append a new value to it.

See proof-of-concept code below:

let orders = [
  {n: 0, orderId: null, lat: 49.396315, lng: 15.609607, type: "start"}, 
  {n: 2.5, orderId: 26889, lat: 48.98951, lng: 14.45937, type: "odes"}, 
  {n: 9, orderId: 26969, lat: 50.0823536, lng: 12.3791268, type: "odes"}, 
  {n: 9, orderId: 26995, lat: 50.0823536, lng: 12.3791268, type: "odes"}, 
  {n: 31, orderId: null, lat: 50.7343366, lng: 15.0501002, type: "end"}, 
  {n: 31, orderId: 26969, lat: 50.7343366, lng: 15.0501002, type: "prij"}, 
  {n: 31, orderId: 26995, lat: 50.7343366, lng: 15.0501002, type: "prij"},
];

// Receives a maybe array and pushes a new entry into it
function pushNewEntry(target, entry) {
  if (!Array.isArray(target)) {
    return [target, entry];
  } else {
    target.push(entry);
    return target;
  }
}

const groupedOrders = orders.reduce((acc, cur) => {
  const { n, orderId, lat, lng, type } = cur;

  // Find existing entry based on matching `n`, `lat`, and `lng`
  const existingEntry = acc.find(x => {
    return x.n === n && x.lat === lat && x.lng === lng;
  });

  if (!existingEntry) {
    acc.push(cur);
  } else {
    existingEntry.orderId = pushNewEntry(existingEntry.orderId, orderId);
    existingEntry.type = pushNewEntry(existingEntry.type, type);
  }
  
  return acc;
}, []);

console.log(groupedOrders);

Upvotes: 1

Related Questions