MattG
MattG

Reputation: 1932

How to filter javascript object with dynamic filter

I have a javascript object, and array of id's:

var ids = [46,44,49,47];
var obj = {
        "46": {
            "group": "A",
            "temp": 26,
            "humid": 36
        },
        "44": {
            "group": "B",
            "temp": 19,
            "humid": 32
        },
        "49": {
            "group": "B",
            "temp": 20,
            "humid": 31
        },
        "47": {
            "group": "A",
            "temp": 24,
            "humid": 32
        }
    };

I want to get the average 'temp' of each 'group'. How can I do this?

I found other examples that would solve this problem, however, these examples assume that 'group' will always be the same. In my case, 'group' is always changing.

So essentially I need to find how many unique groups exist, then return the average 'temp' value of each.

I attempted to use some for loops nested together, but it got complicated quickly...

Expected Output: avgTemps = {"A":25, "B": 19.5}

Upvotes: 1

Views: 77

Answers (7)

Mister Jojo
Mister Jojo

Reputation: 22320

you can do that:

const
  ids = [46,44,52,49,47]
, obj = 
    { '46': { group: 'A', temp: 26, humid: 36 } 
    , '44': { group: 'B', temp: 19, humid: 32 } 
    , '49': { group: 'B', temp: 20, humid: 31 } 
    , '47': { group: 'A', temp: 24, humid: 32 }
    , '64': { group: 'A', temp: 25, humid: 32 }
    }
, avgTemps =
    ids.reduce((rO,key)=>{if (obj[key]) rO.push(obj[key]);return rO},[])
      .reduce((res,o,_,Oe)=>
      {
      if(!res[o.group])
        {
        let grp = Oe.filter(x=>x.group===o.group)
                    .map(y=>y.temp)
        res[o.group] = grp.reduce((sum,t)=>sum+t,0) / grp.length
        }
      return res
      },{})


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

Upvotes: 0

Ehtesham Ahmad Nadim
Ehtesham Ahmad Nadim

Reputation: 431

First of all, I have taken all the unique groups in a set for distinction. Then created an array of objects is manipulated and eventually all the average, count, and the total is assigned in the array of objects. And the expected result is generated.

  var ids = [46, 44, 49, 47];
  var obj = {
    "46": {
      "group": "A",
      "temp": 26,
      "humid": 36
    },
    "44": {
      "group": "B",
      "temp": 19,
      "humid": 32
    },
    "49": {
      "group": "B",
      "temp": 20,
      "humid": 31
    },
    "47": {
      "group": "A",
      "temp": 24,
      "humid": 32
    }
  };
  var groups = new Set()
  Object.keys(obj).forEach(element => {
    groups.add(obj[element].group);
  });
  var tempAvgByGroups = [];
  const avgTemp = {}
  groups.forEach(element => {
    const groupObj = {
      "group": element,
      "average": 0,
      "count": 0,
      "total": 0,
    }
    Object.keys(obj).forEach(item => {
      if (element === obj[item]["group"]) {
        groupObj["count"] = groupObj["count"] + 1;
        groupObj["total"] = groupObj["total"] + obj[item]["temp"]
      }
    });
    groupObj["average"] = groupObj["total"] / groupObj["count"];
    tempAvgByGroups.push(groupObj);
    avgTemp[element] = groupObj["average"]
  });
  console.log(avgTemp);

Upvotes: 0

Alexander Alexandrov
Alexander Alexandrov

Reputation: 1372

One more example in functional style)

function avg(vals: number[]): number {
    return vals.reduce((a, b) => a + b) / vals.length;
}

function groupBy<Item, Desc extends string | number>(descriminator: (item: Item) => Desc) {
    return (items: Item[]): Record<Desc, Item[]> => items.reduce((groups, item) => ({
        ...groups,
        [descriminator(item)]: [...(groups[descriminator(item)] || []), item]
    }), {} as Record<Desc, Item[]>);
}

function mapValues<From, To>(mapper: (from: From) => To) {
    return (record: Record<string, From>): Record<string, To> => Object.assign(
        {} as Record<string, To>,
        ...Object.keys(record).map(key => ({
            [key]: mapper(record[key])
        }))
    )
}

const groups = groupBy(
    ({ group }: Item) => group
)(
    Object.values(obj)
);

const avgTemps = mapValues(
    (groupItems: Item[]) => avg(groupItems.map(({ temp }) => temp))
)(
    groups
);

TS Playground

Upvotes: 0

trincot
trincot

Reputation: 350310

Create the target object first with sums and counts, and then divide those to averages:

function getAvgTemp(obj) {
    let data = Object.values(obj);
    let result = Object.fromEntries(data.map(({group}) => [group, { sum: 0, count: 0 }]));
    for (let {group, temp} of data) {
        let o = result[group];
        o.count++; 
        o.sum += temp;
    }
    for (let key of Object.keys(result)) {
        result[key] = result[key].sum / result[key].count;
    }
    return result;
}

// Demo    
var obj = {"46": {"group": "A","temp": 26,"humid": 36},"44": {"group": "B","temp": 19,"humid": 32},"49": {"group": "B","temp": 20,"humid": 31},"47": {"group": "A","temp": 24,"humid": 32}};
let avgTemp = getAvgTemp(obj);
console.log(avgTemp);

Note that the ids array seems overkill, since the properties of the object can be collected with Object.keys.

Upvotes: 1

Pavlo
Pavlo

Reputation: 649

This should be a little bit shorter:

 let output = {};
 
 for (let group in obj) {
        const groupName = obj[group]['group'];
        if(!output[groupName]) {
            output[groupName] =  obj[group]['temp'];
         } else {
            output[groupName] = (output[groupName] + obj[group]['temp'])/2;
         }
 }
 console.log(output);

Upvotes: 1

kstepien
kstepien

Reputation: 1233

You can use Object.keys(obj) to get each ids of the group.

Then you can get values from this group like this:

const obj = {
        "46": {
            "group": "A",
            "temp": 26,
            "humid": 36
        },
        "44": {
            "group": "B",
            "temp": 19,
            "humid": 32
        },
        "49": {
            "group": "B",
            "temp": 20,
            "humid": 31
        },
        "47": {
            "group": "A",
            "temp": 24,
            "humid": 32
        }
    };

Object.keys(obj).map((id) => console.log(`Temp of id ${id} is ${obj[id].temp}`))

We can create the object with group values.

Now if you can map your obj, let's create the average value of each group.

const obj = {
            "46": {
                "group": "A",
                "temp": 26,
                "humid": 36
            },
            "44": {
                "group": "B",
                "temp": 19,
                "humid": 32
            },
            "49": {
                "group": "B",
                "temp": 20,
                "humid": 31
            },
            "47": {
                "group": "A",
                "temp": 24,
                "humid": 32
            }
        };
const groups = {};

Object.keys(obj).map((id) => {
  if(!!groups[obj[id].group]) {
    groups[obj[id].group].push(obj[id].temp);
  } else {
    groups[obj[id].group] = [obj[id].temp]
  }
})

console.log(groups)

Then finally we can count average temps.

const obj = {
            "46": {
                "group": "A",
                "temp": 26,
                "humid": 36
            },
            "44": {
                "group": "B",
                "temp": 19,
                "humid": 32
            },
            "49": {
                "group": "B",
                "temp": 20,
                "humid": 31
            },
            "47": {
                "group": "A",
                "temp": 24,
                "humid": 32
            }
        };
const groups = {};

Object.keys(obj).forEach((id) => {
  if(!!groups[obj[id].group]) {
    groups[obj[id].group].push(obj[id].temp);
  } else {
    groups[obj[id].group] = [obj[id].temp]
  }
})

function getAvg(grades) {
  const total = grades.reduce((acc, c) => acc + c, 0);
  return total / grades.length;
}

Object.keys(groups).forEach((group) => {
  groups[group] = getAvg(groups[group])
})

console.log(groups)

Upvotes: 1

jboockmann
jboockmann

Reputation: 1025

In a first step, you obtain a list of unique group names present in the data. Then, you filter out the temperatures for each group and compute the average.

You can also use lodash functions for removing duplicate entries from a list (_.uniq) and to sum an array of numbers (_.sum).

var ids = [46,44,49,47];
var obj = {
        "46": {
            "group": "A",
            "temp": 26,
            "humid": 36
        },
        "44": {
            "group": "B",
            "temp": 19,
            "humid": 32
        },
        "49": {
            "group": "B",
            "temp": 20,
            "humid": 31
        },
        "47": {
            "group": "A",
            "temp": 24,
            "humid": 32
        }
    };
function onlyUnique(value, index, self) {
  return self.indexOf(value) === index;
}

let groups = Object.values(obj).map((o)=>o.group).filter(onlyUnique)
for (let group of groups) {
  let temps = Object.values(obj).filter((o)=>o.group===group).map((o)=>o.temp)
  let avgtmp = temps.reduce((pv, cv) => pv + cv, 0)/temps.length;
  console.log(`group ${group} has an avg tmp of ${avgtmp}`)
}

// OUTPUT
// group B has an avg tmp of 19.5
// group A has an avg tmp of 25

Upvotes: 1

Related Questions