Doe
Doe

Reputation: 193

How to filter an object of objects efficiently?

This question has been proposed many times in SO, but they all refer to an array of objects.

In my case, I would like to filter an object of objects.

Say I have this object:

"Users": {
  "w14FKo72BieZwbxwUouTpN7UQm02": {
    "name": "Naseebullah Ahmadi",
    "userType": "Patient",
    "writePermission": false
  },
  "SXMrXfBvexQUXfnVg5WWVwsKjpD2": {
    "name": "Levi Yeager",
    "userType": "Patient",
    "writePermission": false
  },
  "VoxHFgUEIwRFWg7JTKNXSSoFoMV2": {
    "name": "Ernest Kamavuako",
    "userType": "Doctor",
    "writePermission": true
  },
  "hFoWuyxv6Vbt8sEKA87T0720tXV2": {
    "name": "Karla Stanlee",
    "userType": "Doctor",
    "writePermission": true
  }
}

I would like to filter this so I can get the following:

"UsersCustom": {
  "w14FKo72BieZwbxwUouTpN7UQm02": {
    "name": "Naseebullah Ahmadi",
    "userType": "Patient",
    "writePermission": false
  },
  "SXMrXfBvexQUXfnVg5WWVwsKjpD2": {
    "name": "Levi Yeager",
    "userType": "Patient",
    "writePermission": false
  }
}

What's the point of doing this?

Note that this object "User" is huge in reality (over 1000 entries) and each user has more attributes than mere "name", "userType", and "writePermission".

The reason why I need to filter the user's object is so that I can get users who are only (Patient) and get the id of that Patient to lookup in another object and finally merge them all in one object.

What I have so far

// user is the object as seen above
let Patients = users ? (
  // I loop through them
  Object.keys(users).map((uid, i) => {
    // get individual patient
    const patient = users[uid];
    // check their userType
    if (patient.userType === "Patient") {
      let customPatient = {
        uid: uid,
        name: patient.name,
        profession: patient.profession,
        history: null,
        ecg: null,
        heartSound: null
      };

      this._healthRef(uid).then(health => {
        customPatient.history = health;
        return this._heartSoundRef(uid).then(HS => HS);
      }).then(HS => {
        customPatient.heartSound = HS;
        return this._ecgRef(uid).then(a => a);
      }).then(ecg => {
        customPatient.ecg = ecg;
      }).then(() => {
        cusomPatients.push(customPatient);
      })
    }
  })
)

My solution above is, although partially complete, is still not efficient and wrong. Because I need the Id of each patient for other lookup

Update

Currently, most solution provides looping through the entries, which means that it will run O(n) in worst case. Is there any possibility that it can be solved quicker than O(n)?

Upvotes: 2

Views: 3808

Answers (5)

Slai
Slai

Reputation: 22876

My guess is that filtering during parsing might be most efficient (haven't measured/compaed it):

j = '{"w14FKo72BieZwbxwUouTpN7UQm02":{"name":"Naseebullah Ahmadi","userType":"Patient","writePermission":false},"SXMrXfBvexQUXfnVg5WWVwsKjpD2":{"name":"Levi Yeager","userType":"Patient","writePermission":false},"VoxHFgUEIwRFWg7JTKNXSSoFoMV2":{"name":"Ernest Kamavuako","userType":"Doctor","writePermission":true},"hFoWuyxv6Vbt8sEKA87T0720tXV2":{"name":"Karla Stanlee","userType":"Doctor","writePermission":true}}'

o = JSON.parse(j, (k, v) => !v.userType || v.userType === 'Patient' ? v : void 0)

console.log(o)

Caching the filtered object would be even more efficient to reduce network traffic.

Upvotes: 1

fyasir
fyasir

Reputation: 2970

Using reduce method you can get your object

var data = {
  "w14FKo72BieZwbxwUouTpN7UQm02": {
    "name": "Naseebullah Ahmadi",
    "userType": "Patient",
    "writePermission": false
  },
  "SXMrXfBvexQUXfnVg5WWVwsKjpD2": {
    "name": "Levi Yeager",
    "userType": "Patient",
    "writePermission": false
  },
  "VoxHFgUEIwRFWg7JTKNXSSoFoMV2": {
    "name": "Ernest Kamavuako",
    "userType": "Doctor",
    "writePermission": true
 },
 "hFoWuyxv6Vbt8sEKA87T0720tXV2": {
   "name": "Karla Stanlee",
   "userType": "Doctor",
   "writePermission": true
 }

};

var results = Object.keys(data).reduce(function(acc, val) {
    if(data[val].userType === 'Patient')  acc[val] = data[val];
  return acc;
}, {});
console.log(results);

Upvotes: 5

void
void

Reputation: 36703

Use for...in to iterate through object and check if ther usertype is patient or not.

var users = {
  "Users": {
    "w14FKo72BieZwbxwUouTpN7UQm02": {
      "name": "Naseebullah Ahmadi",
      "userType": "Patient",
      "writePermission": false
    },
    "SXMrXfBvexQUXfnVg5WWVwsKjpD2": {
      "name": "Levi Yeager",
      "userType": "Patient",
      "writePermission": false
    },
    "VoxHFgUEIwRFWg7JTKNXSSoFoMV2": {
      "name": "Ernest Kamavuako",
      "userType": "Doctor",
      "writePermission": true
    },
    "hFoWuyxv6Vbt8sEKA87T0720tXV2": {
      "name": "Karla Stanlee",
      "userType": "Doctor",
      "writePermission": true
    }
  }
};

for(let user in users.Users){
  if(users.Users.hasOwnProperty(user) && users.Users[user].userType!=="Patient")
    delete users.Users[user];
}

console.log(users);

This will modify the object, if you want to preserve it then just do a deep copy of the original object.

Upvotes: 0

Nenad Vracar
Nenad Vracar

Reputation: 122087

You can use reduce() method on your object keys and build new object from that.

const obj = {"Users":{"w14FKo72BieZwbxwUouTpN7UQm02":{"name":"Naseebullah Ahmadi","userType":"Patient","writePermission":false},"SXMrXfBvexQUXfnVg5WWVwsKjpD2":{"name":"Levi Yeager","userType":"Patient","writePermission":false},"VoxHFgUEIwRFWg7JTKNXSSoFoMV2":{"name":"Ernest Kamavuako","userType":"Doctor","writePermission":true},"hFoWuyxv6Vbt8sEKA87T0720tXV2":{"name":"Karla Stanlee","userType":"Doctor","writePermission":true}}}

const newObj = {
  'UserCustom': Object.keys(obj.Users).reduce((r, k) => {
    if(obj.Users[k].userType == 'Patient') r[k] = Object.assign({}, obj.Users[k])
    return r;
  }, {})
}
  
console.log(newObj)

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1075199

You can solve this with for-in or Object.keys or (on modern JavaScript engines) Object.entries.

for-in provides a means of iterating through the object's enumerable properties; you can then use those property names to look up the value:

var result = {};
for (var key in users) {
    var entry = users[key];
    if (entry.userType === "Patient") {
        result[key] = entry;
    }
}

Live Example:

var users = {
  "w14FKo72BieZwbxwUouTpN7UQm02": {
    "name": "Naseebullah Ahmadi",
    "userType": "Patient",
    "writePermission": false
  },
  "SXMrXfBvexQUXfnVg5WWVwsKjpD2": {
    "name": "Levi Yeager",
    "userType": "Patient",
    "writePermission": false
  },
  "VoxHFgUEIwRFWg7JTKNXSSoFoMV2": {
    "name": "Ernest Kamavuako",
    "userType": "Doctor",
    "writePermission": true
  },
  "hFoWuyxv6Vbt8sEKA87T0720tXV2": {
    "name": "Karla Stanlee",
    "userType": "Doctor",
    "writePermission": true
  }
};

var result = {};
for (var key in users) {
    var entry = users[key];
    if (entry.userType === "Patient") {
        result[key] = entry;
    }
}

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

Object.keys gives you an array of the object's own enumerable properties, so:

var result = {};
Object.keys(users).forEach(function(key) {
    var entry = users[key];
    if (entry.userType === "Patient") {
        result[key] = entry;
    }
});

Live Example:

var users = {
  "w14FKo72BieZwbxwUouTpN7UQm02": {
    "name": "Naseebullah Ahmadi",
    "userType": "Patient",
    "writePermission": false
  },
  "SXMrXfBvexQUXfnVg5WWVwsKjpD2": {
    "name": "Levi Yeager",
    "userType": "Patient",
    "writePermission": false
  },
  "VoxHFgUEIwRFWg7JTKNXSSoFoMV2": {
    "name": "Ernest Kamavuako",
    "userType": "Doctor",
    "writePermission": true
  },
  "hFoWuyxv6Vbt8sEKA87T0720tXV2": {
    "name": "Karla Stanlee",
    "userType": "Doctor",
    "writePermission": true
  }
};

var result = {};
Object.keys(users).forEach(function(key) {
    var entry = users[key];
    if (entry.userType === "Patient") {
        result[key] = entry;
    }
});

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

Object.entries provides an iterable which consists of the name and value of each own, enumerable property. It's quite new. So for instance (in ES2015+ syntax where Object.entries was added in ES2017 but can be polyfilled):

const result = {};
for (const [key, value] of Object.entries(users)) {
    if (value.userType === "Patient") {
        result[key] = value;
    }
}

Live Example:

const users = {
  "w14FKo72BieZwbxwUouTpN7UQm02": {
    "name": "Naseebullah Ahmadi",
    "userType": "Patient",
    "writePermission": false
  },
  "SXMrXfBvexQUXfnVg5WWVwsKjpD2": {
    "name": "Levi Yeager",
    "userType": "Patient",
    "writePermission": false
  },
  "VoxHFgUEIwRFWg7JTKNXSSoFoMV2": {
    "name": "Ernest Kamavuako",
    "userType": "Doctor",
    "writePermission": true
  },
  "hFoWuyxv6Vbt8sEKA87T0720tXV2": {
    "name": "Karla Stanlee",
    "userType": "Doctor",
    "writePermission": true
  }
};

const result = {};
for (const [key, value] of Object.entries(users)) {
    if (value.userType === "Patient") {
        result[key] = value;
    }
}

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

Upvotes: 0

Related Questions