append values to array inside object based on key in JS - javascript

I have an object and want to append values to arrays inside it based on key.
For eg.
var A = {};
A[room1] = ["1"];
A[room2] = ["2"];
A[room3] = ["3"];
which will be looking like
A = {"room1":["1"], "room2":["2"], "room3":["3"]};
But what I want is that whenever a user gives some value corresponding to the already added key, instead of overwriting the previous value I want to append in it.
If, for example, value came as 101 and I want to add it to key room1 in such a way that later these values can be retrieved easily.
So the whole object this time becomes
A = {"room1":["1","101"], "room2":["2"], "room3":["3"]};
Now if I want to add 201 to key room2 , it will be:
A = {"room1":["1","101"], "room2":["2","201"], "room3":["3"]};
What I have I tried?
I have an array. I don't want to use many arrays.
var arr = [];
whenever value cam I push it to the array
arr.push(value);
But pushing it to the array leads to adding values to all not to the corresponding key

[[The first part of this answer is based on a previous version of the OP's question which has now been edited to a different problem. See the second part of this answer for the solution which applies to the current edit of the question (It really messes things up when the whole question gets changed to something else.]]
Original Answer
You just have to test if the key already exists and examine if there's already an array there. If the key doesn't exist, add it. If the key exists and it's already an array, just push another value into the array. If the key exists, but it's not an array, grab the value and then reset the key to an array with the first two values in it.
Here's code to do that:
function addValue(obj, key, value) {
if (obj.hasOwnProperty(key)) {
// check if it's already an array using the recommended way of detecting an array
if (Object.prototype.toString.call(obj[key]) === "[object Array]")
obj[key].push(value);
} else {
var firstVal = obj[key];
obj[key] = [firstVal, value];
}
} else {
obj[key] = value;
}
}
Latest Answer
FYI, your data structure choice is difficult to both read and write because both reader and writer have to check the type of a value before they can operate on it. It would be much easier if items were just always arrays with one or more elements in them like this.
// one item for each key
A = {"room1":["1"], "room2":["2"], "room3":["3"]};
// add 101 to room 1
A = {"room1":["1","101"], "room2:["2"], "room3":["3"]};
// add 201 to room 2
A = {"room1":["1","101"], "room2":["2","201"], "room3":["3"]};
Then, you would need any special code to read and to write, you'd just check if the key exists and if so, push a new value into it. If not, add the array.
In this case, adding a value would just be this
function addValue(obj, key, value) {
if (obj.hasOwnProperty(key)) {
obj[key].push(value);
} else {
obj[key] = [value];
}
}

try this
function pushValue(obj, key, value)
{
if(obj.hasOwnProperty(key) {
var currentVal = obj[key];
if(currentVal instanceof Array)
obj[key].push(value);
else
obj[key] = [currentVal, value];
} else {
alert("No such key.");
}
}

Your requirement can be achieved in this way too.
function pushValue(obj, key, value)
{
if(obj[key])
{
if(obj[key].push)
{
obj[key][obj[key].length] = value;
}
else
{
var xContainedVal = obj[key];
obj[key] =[xContainedVal, value];
}
}
else
{
alert("Key Not Found!");
}
}
Updated:
function pushValue(obj, key, value)
{
if(obj[key])
{
obj[key][obj[key].length] = value;
}
else
{
obj[key] =[value];
}
}
A working demo

Related

Search through literal object and all its nested objects (up to nth level) with a generalized function [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Improve this question
Yet, another question about JSON-like Object literals.
the object that I work with has the following structure:
let family ={
"id":"someId",
"participants":[
{
"name":"Q",
"sex":"m",
"age":23,
"occupations":[
{
"jobId":"some ID"
"finished": true,
"start": dateTime,
"end":dateTime,
"skills":[
{
"name":"welding"
},
{
"name":"concrete mixing"
},
]
},
{
"jobId": "someId",
"finished": false,
"skills":[
{
"name":"power lifting"
},
{
"name":"swimming"
},
{
}
]
},
{"OTHER Participant"},
{"OTHER Participant"}
]
}
It is for the sake of example.
when I receive data, every literal object is going to be unique so there is no "schema" that I can create and refer to based on the type, for example.
And I need to find the unique objects on the 2nd, 3rd, 4th etc levels.IF they exist
My take on this:
Every time I would try to use something either like :
let personIneed; //to save him later;
let part = family.participants;
for (const xx of part){
let partJobs =xx.occupations;
for (const xxx of partJobs){
if(xxx.end && xxx.finished == true){
let partJobSkills = xxx.skills;
let isSkillPresent =false; //to change to true once skill iteration is finished
for (const xxxx of partJobSkills){
if(xxxx.name ==="concrete mixing"){
isSkillPresent =true;
}
}
//check if skill's there and save the participant
if(isSkillPresent){
personIneed = xx;
return;
}
}
}
}
Which is very bulky and if i have to build a function like that for every set of criteria... well I'd rather build a guillotine. It also looks a little bit like a callback Hell to me (the way it cascades and only) :)
Either when the search is only at the high level,
let part = family.participants;
let man = part.find (p=>p.sex==="m");
Which would only return me the first person to match the criteria. which is not ideal.
I would like to build a generalized function that will be able to go through the Object literals finding the nested objects that match my criteria. The criteria should be passed as parameters of the function. Function should also consider that there can be several nested objects with the same keys ( for example one person can be a Builder on many different jobs) or there can be in fact none of the objects that match the criteria.
The ultimate goal is somehow to make all the nested objects easily accessible when the object is found. (for example when we found a person who has worked as a builder and who has finished his contract, we return this object but if i need to find the start and end of his working period, I need to go through all the nested objects again and it becomes hell again)
Please note that some arrays will simply not exist and some will have one value.
From your comments, it sounds like you're looking for a generalized way to loop through nested objects and arrays. You'd do that with recursion. If you want to make it possible to collect information, or to stop the operation, in different ways for different places you use it, you can pass in a callback function.
Here's an example:
// A version of `Object.prototype.hasOwnProperty` that we can call with
// any object and key. For arrays, we use this to check for empty slots
// (holes in a sparse arary). For non-array objects, we use this to skip
// inherited properties; in the not-array path below, you could remove
// the `hasOwn` check if you wanted to process inherited properties (but
// probably best to keept it for the array path).
const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
// The deep search function
function deepSearch(obj, callback) {
if (Array.isArray(obj)) {
// It's an array, loop through it
for (let index = 0, len = obj.length; index < len; ++index) {
// Is there an entry at this index?
if (hasOwn(obj, index)) {
// Yes, get its value
const value = obj[index];
// Call the callback
if (callback(obj, index, value)) {
// Callback returned a truthy value, stop here and
// return true
return true;
}
// Is this value an object?
if (value && typeof value === "object") {
// Yes, recurse
if (deepSearch(value, callback)) {
// Recursion found it, stop here
return true;
}
}
}
}
} else {
// It's not an array, loop through the object keys
for (const key in obj) {
// Is this an "own" property (not inherited)?
if (hasOwn(obj, key)) {
// Yes, get its value
const value = obj[key];
// Callback the callback
if (callback(obj, key, value)) {
// Callback returned a truthy value, stop here and
// return true
return true;
}
// Is this value an object?
if (value && typeof value === "object") {
// Yes, recurse
if (deepSearch(value, callback)) {
// Recursion found it, stop here
return true;
}
}
}
}
}
// Not found, return false
return false;
}
Here's a live version using a callback to find something specific and stop the iteration:
const example = {
first1: "value of first1",
first2: {
second1: "value of second1",
second2: [
{
third11: "value of third11",
third12: {
fourth11: "value of fourth11",
},
},
{
third21: "value of third21",
third22: {
fourth21: "value of fourth21",
},
},
],
},
};
// A version of `Object.prototype.hasOwnProperty` that we can call with
// any object and key. For arrays, we use this to check for empty slots
// (holes in a sparse arary). For non-array objects, we use this to skip
// inherited properties; in the not-array path below, you could remove
// the `hasOwn` check if you wanted to process inherited properties (but
// probably best to keept it for the array path).
const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
// The deep search function
function deepSearch(obj, callback) {
if (Array.isArray(obj)) {
// It's an array, loop through it
for (let index = 0, len = obj.length; index < len; ++index) {
// Is there an entry at this index?
if (hasOwn(obj, index)) {
// Yes, get its value
const value = obj[index];
// Call the callback
if (callback(obj, index, value)) {
// Callback returned a truthy value, stop here and
// return true
return true;
}
// Is this value an object?
if (value && typeof value === "object") {
// Yes, recurse
if (deepSearch(value, callback)) {
// Recursion found it, stop here
return true;
}
}
}
}
} else {
// It's not an array, loop through the object keys
for (const key in obj) {
// Is this an "own" property (not inherited)?
if (hasOwn(obj, key)) {
// Yes, get its value
const value = obj[key];
// Callback the callback
if (callback(obj, key, value)) {
// Callback returned a truthy value, stop here and
// return true
return true;
}
// Is this value an object?
if (value && typeof value === "object") {
// Yes, recurse
if (deepSearch(value, callback)) {
// Recursion found it, stop here
return true;
}
}
}
}
}
// Not found, return false
return false;
}
deepSearch(example, (obj, key, value) => {
console.log(`Looking at ${key}, value: ${Array.isArray(value) ? "(array)" : value && typeof value === "object" ? "(object)" : JSON.stringify(value)}`);
if (key === "third21") {
console.log(`*** Found third21, value = ${value}`);
return true;
}
});
.as-console-wrapper {
max-height: 100% !important;
}

how to insert new object in node js array if key not exist

I want to create data structure like that.
Var ans =[{"b":[1,2]},{"g":[100,2]}]
I want to create a new object within list if key not exists in list ans.
Else if key exists in one object of ans list then I want to add new values into the object of ans list
For Example:
Example 1) new data c:{2000}
then
Var ans =[{"b":[1,2]},{"g":[100,2]},{c:[2000]}]
Example 2) new data g:{50}
then
Var ans =[{"b":[1,2]},{"g":[100,2,500]},{c:[2000]}]
I am a beginner in node js, understand array, object concept, but not getting exact logic!
Thanks!
You can try following:
Logic
Filter array based on key
Check if object with mentioned key exists or not.
If yes, push value to this array.
If not, create a dummy object and push this object to original array.
Correction, when you do .push({key: value}), key will be considered as string.
Alternates
If you are using ES6, .push({ [key] : value })
Create a dummy object var o = {}. Set key and value to it o[key] = value and push this object.
Optimisations
Instead of setting value like obj[key] = value, since we will be operating on arrays, try obj[key] = [].concat(value). This will enable you to pass value as number or array of values.
Instead of checking the existence of value in .filter, try Array.isArray to check if value exists and is of type array.
Custom function
function checkAndPush(array, key, value) {
var filteredList = array.filter(function(o) {
return Array.isArray(o[key]);
});
filteredList.length > 0 ? filteredList[0][key].push(value) : array.push({
[key]: [].concat(value)
});
return array;
}
var ans =[{"b":[1,2]},{"g":[100,2]}]
console.log(checkAndPush(ans, "c", [2,3]))
console.log(checkAndPush(ans, "c", 4));
Prototype function
Array.prototype.checkAndPush = function(key, value) {
var filteredList = this.filter(function(o) {
return Array.isArray(o[key]);
});
var dummy = {}
dummy[key] = [].concat(value)
filteredList.length > 0 ? filteredList[0][key].push(value) : this.push(dummy);
// or ES6: this.push({ [key]: [].concat(value) })
return this;
}
var ans =[{"b":[1,2]},{"g":[100,2]}]
console.log(ans.checkAndPush("c", [2,3]))
console.log(ans.checkAndPush("c", 4));
If you are dealing with objects as your values
ans[key] = ans[key] || []
ans[key].push(value)
Note, this works because your values will be an array. If they could be primatives then you would use hasOwnProperty to check.
if (ans.hasOwnProperty(key)) {
// Add this to your key somehow
} else {
// initialize the key with your value
}
Node.js is nothing but a library built on javascript. You can do anything using javascript type of progmming. However push and pop method should be able to help you to deal with nodejs array.
ans[key].push(value)

Most efficient way to remove a list of values from a javascript object by keyname

I need to find the most efficient way to remove values from a arbitrarily nested javascript object based on a list of 'keys-to-remove'. i.e.
var obj = {a:1, b:2, c:{d:1, e:1}};
var ignoreList = ["a","e"] (could also be ['a', 'c.e'])
removeIgnoredValues(obj, ignoreList) => {b:2, c:{d:1}}.
Now obviously this is easy enough to do if you don't care about efficiency, and my current implementation has been serving me well up till now. But now I'm having to deal with objects that have 6 levels and large arrays of data.
If anyone has a solution or link to one that would be awesome :)
Cheers
EDIT: Current implementation looks like this. It works (and deals with circular references). But is too slow.
/**
* Returns a sanitised string of an object, removing any functions and unwanted properties.
* #param {int} obj. The object to be stringified
* #param {Array[]} ignoreList. A array of object properties that should be removed.
*/
function sanitise(obj, ignoreList){
if(obj == undefined){
throw "Can't sanitise an undefined object"
}
var entry = JSON.parse(JSON.stringifyOnce(obj));
for(var i in entry){
if(entry.hasOwnProperty(i)){
if(contains(ignoreList, i)){
delete entry[i];
} else if(typeof(entry[i]) == "object" && entry[i] != null){
entry[i] = sanitise(entry[i], ignoreList);
}
}
}
return entry;
}
JSON.stringifyOnce = function(obj, replacer, indent){
var printedObjects = [];
var printedObjectKeys = [];
function printOnceReplacer(key, value){
var printedObjIndex = false;
printedObjects.forEach(function(obj, index){
if(obj===value){
printedObjIndex = index;
}
});
if ( key == ''){ //root element
printedObjects.push(obj);
printedObjectKeys.push("root");
return value;
}
else if(printedObjIndex+"" != "false" && typeof(value)=="object"){
if ( printedObjectKeys[printedObjIndex] == "root"){
return "(pointer to root)";
}else{
return "(see " + ((!!value && !!value.constructor) ? value.constructor.name.toLowerCase() : typeof(value)) + " with key " + printedObjectKeys[printedObjIndex] + ")";
}
}else{
var qualifiedKey = key || "(empty key)";
printedObjects.push(value);
printedObjectKeys.push(qualifiedKey);
if(replacer){
return replacer(key, value);
}else{
return value;
}
}
}
return JSON.stringify(obj, printOnceReplacer, indent);
};
Okay, figured out a pretty nice way. You just make an ignore list with roughly the same object structure as the object to be ignored.
function ignore(obj, list){
for(var i in list){
var type = Object.prototype.toString.call(list[i]);
if(type == "[object String]"){
delete obj[i];
}
else if (type == "[object Object]"){
ignore(obj[i], list[i])
}
else if (type == "[object Array]"){
var objList = obj[i];
var subList = list[i][0];
for(var n in objList){
ignore(objList[n], subList)
}
}
}
}
x = {a:1, b:[{c:1, d:1}, {c:1, d:1}, {c:1, d:1}], e:1}
ignoreList = {'e':'e', 'b':[{'c':'c'}]}
ignore(x, ignoreList) => {a:1, b:[{d:1}, {d:1}, {d:1}]}
We can create a look-up table object for the original object, to delete any given key in O(1) time. The implementation will involve you adding custom functions to add/remove from the object.
(function() {
var lookUpTable = {};
myObj.prototype.insert = function(key, value) {
// add key to the myObj
// insert an Entry for parent of key in lookUpTable
// lookUpTable = { "a" : [myObj.b, myObj, myObj.c.d.e] }
}
myObj.prototype.ignore = function(ignoreList) {
for( key in ignoreList ) {
for( parent in lookUpTable[key] )
delete parent[key];
delete lookUpTable [key];
}
}
}());
Now you can call the insert function to insert the key-value:
myObj.insert('a.b.c.d', ['p', 'g']);
and call the ignore function to delete the object:
myObj.ignore(['d', 'e']);
Sorry for just giving the incomplete code. But, you should be able to implement the details quiet easily. Hope you get the idea.
For the example given by you:
obj = {a:[{b:1, c:1}, {b:1, c:1}, {b:1, c:1}]
and you want to ignore all the 'b's. Note that the lookup table entry values were arrays and not just a single value. This is where the power of ignoring multiple entries with the same name, comes in. In this case, the entry for 'b' would be something like this.
lookupTable = {
b : [ // The parent objects of 'b'
obj['a'][0],
obj['a'][1],
obj['a'][2]
]
}
Basically, the lookuptable holds an array of references to all the objects that contain the key 'b'. So, you iterate through each of these parent objects and delete their 'b' entry.
$.each(lookupTable['b'], function( parent ) {
delete parent['b']; // Deletes 'b' inside of every parent object
});
You populate this lookup table entry while inserting into obj or while obj is loaded for the first time. If obj is hard-coded, you could also generate the lookupTable once and hard-code it. Probably in along with your minify Javascript scripts. Although populating it at run-time is also quiet fine.

Building and maintaining an options array/object in JavaScript

I'm building a faceted/filtering search for a project I'm currently working on and I'm looking for a function (or jquery plugin) to help me do this. Let's say I have an object like the following:
var options = {
"country": 2,
"Gender": 1
}
I then have a function called filter(). I pass filter the key and value of the option I want to toggle/add/update. My current function is causing me nothing but trouble and I'm too far down the debugging rabbit hole to make it worth posting. Essentially I have this:
filter('country', 3);
would update the 'country' key to have '3' as its new value.
filter('Age Group', 4);
would add that key/value pair to the options object.
filter('Gender', 1);
would remove the 'Gender' key from the object completely.
How about this obvious implementation:
​function filter(o, key, val)​ {
if (o.hasOwnProperty(key) && o[key] == val) {
delete o[key];
} else {
o[key] = val;
}
}
?
I'd suggest this:
​function filter(o, key, val)​ {
if (o[key] === val) {
delete o[key];
} else {
o[key] = val;
}
}
If the key does not exist as a property of the object, then o[key] will be undefined which will not match val so it will add the key/value.
If the key exists, but the val does not match, it will set the key/value
If the key exists and the val matches, it will remove the key.
Note: it's important to use === to make sure there's no auto-type conversion in the comparison.
Or, if you want a function that automatically works with your options variable, then you could use this:
​function filter(key, val)​ {
if (options[key] === val) {
delete options[key];
} else {
options[key] = val;
}
}
Personally, I think code is more readable if you make explicit setKey and removeKey functions rather than this variable operation based on whether the key previously exists or whether it matches.

How do you recursively remove nested objects that contain an empty array?

I initially receive an AJAX response of {"B":{"1":"100","3":{"AA":256}},"A":100} and converted to a javascript object:
var jsonOBJ = {};
jsonOBJ = jQuery.parseJSON(data);
Future responses can be subsets or supersets of the initial response. If the value of a table is unchanged at the server, the stagnant data is replaced with an empty array. Example:
{"B":{"1":"90","2":200,"3":[]}}
{"B":[],"A":20}
Everytime an AJAX response is received, the object is updated with:
jQuery.extend(true, jsonOBJ, jQuery.parseJSON(data));
But I need the javascript object to keep the unchanged portions, so I need to end up with an object that would be equivalent to the following with the example responses above:
jsonOBJ = jQuery.parseJSON('{"B":{"1":"90","2":200,"3":{"AA":256}},"A":20}');
My preferred option would be to remove the empty objects from the converted response. Is there an existing function or a modification to the jQuery extend function that would do this?
You can remove the elements in your response with empty arrays with this code.
It cycles through the top level, looking for any empty arrays and removing them. Any objects it finds, it recurses into to also remove empty arrays in them:
// make sure the ES5 Array.isArray() method exists
if(!Array.isArray) {
Array.isArray = function (arg) {
return Object.prototype.toString.call(arg) == '[object Array]';
};
}
function removeEmptyArrays(data) {
for (var key in data) {
var item = data[key];
// see if this item is an array
if (Array.isArray(item)) {
// see if the array is empty
if (item.length == 0) {
// remove this item from the parent object
delete data[key];
}
// if this item is an object, then recurse into it
// to remove empty arrays in it too
} else if (typeof item == "object") {
removeEmptyArrays(item);
}
}
}
var jsonOBJ = {};
jsonOBJ = jQuery.parseJSON(data);
removeEmptyArrays(jsonOBJ);
You can see it work here: http://jsfiddle.net/jfriend00/U6qMH/
Not really what I asked for, but removing the empty arrays from the JSON string is a solution:
jQuery.extend(true, jsonOBJ, jQuery.parseJSON(data.replace(/"[A-Za-z0-9_]*":\[\]/,'').replace(/{,/,'{').replace(/,}/,'}')));
this will complete the function ;)
removeEmptyArrays(data) {
for (var key in data) {
var item = data[key];
// see if this item is an array
if (Array.isArray(item)) {
// see if the array is empty
if (item.length == 0) {
// remove this item from the parent object
delete data[key];
} else {
this.removeEmptyArrays(item);
}
// if this item is an object, then recurse into it
// to remove empty arrays in it too
} else if (typeof item == "object") {
this.removeEmptyArrays(item);
}
}
},

Categories

Resources