javascriptarraysobjectgrouping

Group an array of objects with a tolerance factor


I have an array of objects with a name and a value that looks like this (sorted by values):

var array = [ 
    { Name: "F", Value: 320 },
    { Name: "E", Value: 321 },
    { Name: "C", Value: 340 },
    { Name: "G", Value: 340 },
    { Name: "A", Value: 400 },
    { Name: "D", Value: 403 },
    { Name: "B", Value: 404 }
]

I want to combine objects with the same value plus a tolerance. Grouping can be done like this:

var groupedArray = Object.groupBy(array, e => e.Value ); // Is it possible to add a tolerance factor here?

This creates groups that have exactly the same values. But what I actually want is a grouping with a certain tolerance. So, if values ​​are just slightly larger than the previous value, they should still be included in the group. The result could then look like this:

{
  320: [{
    Name: "F",
    Value: 320
  }, {
    Name: "E",
    Value: 321
  }],
  340: [{
    Name: "C",
    Value: 340
  }, {
    Name: "G",
    Value: 340
  }],
  400: [{
    Name: "A",
    Value: 400
  }, {
    Name: "D",
    Value: 403
  }, {
    Name: "B",
    Value: 404
  }]
}

... so all values ​​with a tolerance of, for example, +5 are in one group. Is it possible to add such a tolerance factor?


Solution

  • You could reduce the sorted array by having a look to the difference of each value and predecessor and keep the group if the difference is in a wanted delta/tolerance.

    Finally build entries for a later object.

    const
        tolerance = 5,
        array = [{ Name: "F", Value: 320 }, { Name: "E", Value: 321 }, { Name: "C", Value: 340 }, { Name: "G", Value: 340 }, { Name: "A", Value: 400 }, { Name: "D", Value: 403 }, { Name: "B", Value: 404 }],
        result = Object.fromEntries(array
            .reduce((r, o, i, a) => {
                if (!i || o.Value - a[i - 1].Value > tolerance) r.push([o]);
                else r.at(-1).push(o);
                return r;
            }, [])
            .map(a => [a[0].Value, a])
        );
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }