1

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

2
  • You can just use a reducer and add to accumulator with the condition you have just mentioned. Commented Oct 27, 2020 at 15:22
  • you want to erase orders and set a new one ? Commented Oct 27, 2020 at 15:25

4 Answers 4

1

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

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot, really elegant solution! Just in the first code section, you haven't declared the el ;)
@kybernaut.cz thank you! I was also happy to have had this inspiration for the second solution :)
1

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

1 Comment

Thanks for your help, I've picked the shortest answer as the most elegant, but your solution works nicely as well. Thanks for opening my eyes to the solution!
1

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.

1 Comment

Thanks for your help, I've picked the shortest answer as the most elegant, but your solution works nicely as well. Thanks for opening my eyes to the solution!
1

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

1 Comment

Thanks for your help, I've picked the shortest answer as the most elegant, but your solution works nicely as well. Thanks for opening my eyes to the solution!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.