Join and group the elements of a list into a string according to certain conditions

I have a list of objects that look like this:

[
    { "id": 1, "ifsG0": "1-G01", "price": 200, ... },
    { "id": 2, "ifsG0": "1-G01", "price": 200, ... },
    { "id": 3, "ifsG0": "1-G02", "price": 140, ... },
    { "id": 4, "ifsG0": "1-G03", "price": 90, ... },
    { "id": 5, "ifsG0": "1-G01", "price": 200, ... },
    { "id": 6, "ifsG0": "1-G03", "price": 90, ... },
    { "id": 7, "ifsG0": "1-G02", "price": 140, ... },
    { "id": 8, "ifsG0": "1-G01", "price": 200, ... },
    { "id": 9, "ifsG0": "1-G01", "price": 200, ... },
    { "id": 10, "ifsG0": "1-G03", "price": 90, ... },
    { "id": 11, "ifsG0": "1-G03", "price": 90, ... },
    { "id": 12, "ifsG0": "1-G02", "price": 140, ... },
]

And I would like to get a string like this:

"ifsG0#quantity#price#ifsG0#quantity#price#ifsG0#quantity#price..."

result = "1-G01#5#200#1-G02#3#140#1-G03#4#90"

I have done some research and am getting there with this solution, but I can't find it clean at all. Would there be a way to do it all in one line.

PS: the prices will always be the same for all identical ifsG0s.

var list = [
    { "id": 1, "ifsG0": "1-G01", "price": 200 },
    { "id": 2, "ifsG0": "1-G01", "price": 200 },
    { "id": 3, "ifsG0": "1-G02", "price": 140 },
    { "id": 4, "ifsG0": "1-G03", "price": 90 },
    { "id": 5, "ifsG0": "1-G01", "price": 200 },
    { "id": 6, "ifsG0": "1-G03", "price": 90 },
    { "id": 7, "ifsG0": "1-G02", "price": 140 },
    { "id": 8, "ifsG0": "1-G01", "price": 200 },
    { "id": 9, "ifsG0": "1-G01", "price": 200 },
    { "id": 10, "ifsG0": "1-G03", "price": 90 },
    { "id": 11, "ifsG0": "1-G03", "price": 90 },
    { "id": 12, "ifsG0": "1-G02", "price": 140 },
];

const sortedList = list.sort((a,b) => (a.ifsG0 > b.ifsG0) ? 1 : ((b.ifsG0 > a.ifsG0) ? -1 : 0));
var partNoCounts = sortedList.map(gi => gi.ifsG0).reduce((a, c) => (a[c] = (a[c] || 0) + 1, a), Object.create(null));
var partNo = Object.keys(partNoCounts).map(g0 => g0 + '#' + partNoCounts[g0] + '#' + sortedList.find(gi => gi.ifsG0 === g0).price).join('#');

console.log(partNo);

Thank you

5 answers

  • answered 2021-06-23 07:28 Kinglish

    Here's a reduce that tracks the quantity and maps into the desired result.

    const partNo = Object.values(list.reduce((b, a) => {
       if (b.hasOwnProperty(a.ifsG0)) b[a.ifsG0].quantity++;
       else b[a.ifsG0] = {...a, quantity: 1};
       return b; }, {}))
      .sort((a,b)=>a.ifsG0.localeCompare(b.ifsG0))
      .map(e => [e.ifsG0, e.quantity, e.price].join('#')).join('#')
    

    var list = [{ "id": 1, "ifsG0": "1-G01", "price": 200 },
        { "id": 2, "ifsG0": "1-G01", "price": 200 },
        { "id": 3, "ifsG0": "1-G02", "price": 140 },
        { "id": 4, "ifsG0": "1-G03", "price": 90 },
        { "id": 5, "ifsG0": "1-G01", "price": 200 },
        { "id": 6, "ifsG0": "1-G03", "price": 90 },
        { "id": 7, "ifsG0": "1-G02", "price": 140 },
        { "id": 8, "ifsG0": "1-G01", "price": 200 },
        { "id": 9, "ifsG0": "1-G01", "price": 200 },
        { "id": 10, "ifsG0": "1-G03", "price": 90 },
        { "id": 11, "ifsG0": "1-G03", "price": 90 },
        { "id": 12, "ifsG0": "1-G02", "price": 140 }];
    
    const partNo = Object.values(list.reduce((b,a)=>{ if (b.hasOwnProperty(a.ifsG0)) b[a.ifsG0].quantity++; else b[a.ifsG0] = {...a, quantity: 1}; return b; },{}))
      .sort((a,b)=>a.ifsG0.localeCompare(b.ifsG0))
      .map(e=>[e.ifsG0, e.quantity, e.price].join('#')).join('#')
    
    console.log(partNo);
    console.log("1-G01#5#200#1-G02#3#140#1-G03#4#90 -- desired")

  • answered 2021-06-23 07:33 User863

    Using reduce and flatmap

    var list = [
        { "id": 1, "ifsG0": "1-G01", "price": 200 },
        { "id": 2, "ifsG0": "1-G01", "price": 200 },
        { "id": 3, "ifsG0": "1-G02", "price": 140 },
        { "id": 4, "ifsG0": "1-G03", "price": 90 },
        { "id": 5, "ifsG0": "1-G01", "price": 200 },
        { "id": 6, "ifsG0": "1-G03", "price": 90 },
        { "id": 7, "ifsG0": "1-G02", "price": 140 },
        { "id": 8, "ifsG0": "1-G01", "price": 200 },
        { "id": 9, "ifsG0": "1-G01", "price": 200 },
        { "id": 10, "ifsG0": "1-G03", "price": 90 },
        { "id": 11, "ifsG0": "1-G03", "price": 90 },
        { "id": 12, "ifsG0": "1-G02", "price": 140 },
    ];
    
    var ifs = list.reduce((a, l) => {
      if (!a[l.ifsG0]) a[l.ifsG0] = { p: l.price, q: 0 }
      a[l.ifsG0].q++
      return a
    }, {})
    
    var partNo = Object.keys(ifs).sort().flatMap(i => [i, ifs[i].q, ifs[i].p]).join('#')
    
    console.log(partNo);

  • answered 2021-06-23 07:36 pilchard

    This is just a 'group by' mapped to a string. Doing it in one line isn't necessarily better, but you can simplify it to a single reduce(), move the sort() call to after the to make it more efficient, and then flatMap() and join() the sorted array.

    const
      list = [{ "id": 2, "ifsG0": "1-G01", "price": 200 }, { "id": 1, "ifsG0": "1-G01", "price": 200 }, { "id": 3, "ifsG0": "1-G02", "price": 140 }, { "id": 4, "ifsG0": "1-G03", "price": 90 }, { "id": 5, "ifsG0": "1-G01", "price": 200 }, { "id": 6, "ifsG0": "1-G03", "price": 90 }, { "id": 7, "ifsG0": "1-G02", "price": 140 }, { "id": 8, "ifsG0": "1-G01", "price": 200 }, { "id": 9, "ifsG0": "1-G01", "price": 200 }, { "id": 10, "ifsG0": "1-G03", "price": 90 }, { "id": 11, "ifsG0": "1-G03", "price": 90 }, { "id": 12, "ifsG0": "1-G02", "price": 140 },],
    
      partNo = Object
        .values(
          list.reduce((a, { ifsG0, price }) => (
            (a[ifsG0] ??= { ifsG0, price, count: 0, }).count += 1, a), {})
        )
        .sort((a, b) => a.ifsG0.localeCompare(b.ifsG0))
        .flatMap(({ ifsG0, count, price }) => [ifsG0, count, price])
        .join('#');
    
    console.log(partNo);

  • answered 2021-06-23 07:51 Terry Lennox

    One more way to do this, using Array.reduce again and some destructuring, optional chaining (?.), then Array.flat() and Array.join(), and Object.values:

    const list = [
        { "id": 1, "ifsG0": "1-G01", "price": 200 },
        { "id": 2, "ifsG0": "1-G01", "price": 200 },
        { "id": 3, "ifsG0": "1-G02", "price": 140 },
        { "id": 4, "ifsG0": "1-G03", "price": 90 },
        { "id": 5, "ifsG0": "1-G01", "price": 200 },
        { "id": 6, "ifsG0": "1-G03", "price": 90 },
        { "id": 7, "ifsG0": "1-G02", "price": 140 },
        { "id": 8, "ifsG0": "1-G01", "price": 200 },
        { "id": 9, "ifsG0": "1-G01", "price": 200 },
        { "id": 10, "ifsG0": "1-G03", "price": 90 },
        { "id": 11, "ifsG0": "1-G03", "price": 90 },
        { "id": 12, "ifsG0": "1-G02", "price": 140 },
    ];
    
    const result = Object.values(list.reduce((acc, { ifsG0, price } ) => { 
        return { ...acc, [ifsG0]: [ifsG0, ((acc?.[ifsG0]?.[1]) || 0) + 1 , price ]};
    }, {})).flat().join("#")
    
    console.log(result);

  • answered 2021-06-23 08:05 ikhvjs

    You can use reduceand Map Object to get the quantity. Then use a simple sort and map to get the result.

    const list = [
      { id: 11, ifsG0: "1-G03", price: 90 },
      { id: 1, ifsG0: "1-G01", price: 200 },
      { id: 2, ifsG0: "1-G01", price: 200 },
      { id: 3, ifsG0: "1-G02", price: 140 },
      { id: 4, ifsG0: "1-G03", price: 90 },
      { id: 5, ifsG0: "1-G01", price: 200 },
      { id: 6, ifsG0: "1-G03", price: 90 },
      { id: 7, ifsG0: "1-G02", price: 140 },
      { id: 8, ifsG0: "1-G01", price: 200 },
      { id: 9, ifsG0: "1-G01", price: 200 },
      { id: 10, ifsG0: "1-G03", price: 90 },
      { id: 12, ifsG0: "1-G02", price: 140 },
    ];
    
    const output = [ ...list.reduce( (acc, e) =>
          acc.set(e.ifsG0, { ...e, qty: (acc.get(e.ifsG0)?.qty || 0) + 1 }),
        new Map()
        )]
      .sort()
      .map(s => `${s[1].ifsG0}#${s[1].qty}#${s[1].price}`)
      .join("#");
    
    console.log(output);