Javascript : Add Key & Value at specific index in array of Object - javascript

I have array of Object like this.
let arr = [{name:"abc",age:26},{name:"xyz",age:23},{name:"pqr",age:10}]
let newVal = arr.map(function(el) {
if(el.age > 25){
var o = Object.assign({}, el);
o.gender = 'male';
return o;
}
})
console.log("New Val : " , newVal)
I would like to add {gender:'male'} to object where age is > 25
It says undefined to other objects.
Any help would be great.
Thank You.

You need to return the value if the object doesn't match the condition. Since you haven't retrned anything from from inside map if the condition is not fulfilled, you get undefined for the other objects
let arr = [{
name: "abc",
age: 26
}, {
name: "xyz",
age: 23
}, {
name: "pqr",
age: 10
}]
let newVal = arr.map(function(el) {
if (el.age > 25) {
var o = Object.assign({}, el);
o.gender = 'male';
return o;
}
return el; // return value here
})
console.log("New Val : ", newVal)

issue with your code is already solved in other answer by Shubham, ie when if clause is not executed you are not returning anything.
but i think forEach might be cleaner here
if you want to keep the original array you can copy it using copyArr = [...arr]
let arr = [{name:"abc",age:26},{name:"xyz",age:23},{name:"pqr",age:10}]
arr.forEach(function(el) {
if(el.age > 25)
el.gender = 'male';
})
console.log("New Val : " , arr)

It's missing the return statement when the condition is false.
You can do this in one line using an arrow function as follow:
let arr = [{name:"abc",age:26},{name:"xyz",age:23},{name:"pqr",age:10}],
newVal = arr.map((el) => Object.assign({}, el, el.age > 25 ? {gender: "male"} : {}));
console.log("New Val:", newVal);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You get a new array with map. Inside, you need to take either an copy of the object with a new property or the original object.
let array = [{ name: "abc", age: 26 }, { name: "xyz", age: 23 }, { name: "pqr", age: 10 }],
result = array.map(object => object.age > 25
? { ... object, gender: 'male' }
: object
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Javascript: Remove duplicates in an array by compare function

I have an array with objects like this:
const array = [
{name:'obj1', address: 987, id: '123', location: 'zyx' },
{name:'obj2', address: 654, id: '456', location: 'wvu'},
{name:'obj3', address: 321, id: '123', location: 'zyx'}
];
and I want to remove the duplicates with a function to compare them:
const compareObjects = (a, b) => {
return a.id === b.id && a.location === b.location;
}
The function only compares the relevant properties of the objects.
How can I remove the duplicates from the array with this function?
Edit: To clarify, I want to use a function to compare some properties of the object and not the whole object.
You could reduce the array by checking the object of the temporary result set.
const
array = [{ name:'obj1', address: 987, id: '123', location: 'zyx' }, { name:'obj2', address: 654, id: '456', location: 'wvu' }, { name:'obj3', address: 321, id: '123', location: 'zyx' }],
compareObjects = (a, b) => a.id === b.id && a.location === b.location,
result = array.reduce((r, o) => {
if (!r.some(compareObjects.bind(null, o))) {
r.push(o);
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The obvious solution is to compare each element against each other element (except for itself):
const result = array.filter((el, i) => !arr.some((el2, i2) => i < i2 && compareObjects(el, el2));
However that is O(n²) which will get very slow for large datasets, in that case hashtables help you:
const dupes = new Set;
const key = el => el.id + "|" + el.location;
const result = array.filter(it => !dupes.has(key(el)) && dupes.add(key(el)));
That is O(n) (aka super fast, but consumes more memory).
You can try a function like below
function unique(array , compareObjects){
array.sort(compareObjects);
for(var i = 1; i < array.length; ){
if( compareObjects(array[i-1], array[i]) === 0){
array.splice(i, 1);
} else {
i++;
}
}
return array;
}

sorting when keys are not present javascript using localecompare

I have json like below
[
{name:'aa',age:'1Y',address:'Alaska'},
{name:'cc',age:'4Years',address:'California'},
{name:'mm',address:'Texas'}
]
Whenever I sort with name and address it work but it will throw runtime error if I try to sort with age as it is missing on last entry.
This is my attempt
let obj=[
{name:'aa',age:'2y',address:'Alaska'},
{name:'cc',age:'4y',address:'California'},
{name:'bb',address:'Texas'}
]
let field='age'; //work for name and address;
let mode='string';
if(mode!='number'){
console.log (obj.sort((a, b) => a[field].toString().localeCompare(b[field].toString())));
}
else{
console.log(obj.sort((a, b) => a[field] -b[field]))
}
What is the best way to ignore the entry when keys are not present , do I need to have seperate loop to check keys before sorting . Entry with missing keys will be on the bottom.
Ps: Ages are never >10 years from the business logic and they can come in any format like 1,1Y so it is treated as string
Just make sure you either have the value of the object, or return an empty string.
The shortest code path would be
(a[field] || "")
Where you indicate that if a doesn't have the property, it will treat it as an empty string.
It won't cover for a being null though, so if that can happen, you have to check it more carefully still
let obj = [{
name: 'aa',
age: '25',
address: 'Alaska'
},
{
name: 'cc',
age: '25',
address: 'California'
},
{
name: 'bb',
address: 'Texas'
}
]
let field = 'age'; //work for name and address
console.log(obj.sort((a, b) => (a[field] || "").toString().localeCompare((b[field] || "").toString())));
Another way to do this, would be to simply compare the values (note, again, if a or b would be null, there might be a problem)
let obj = [{
name: 'aa',
age: 25,
address: 'Alaska'
},
{
name: 'cc',
age: 3,
address: 'California'
},
{
name: 'bb',
address: 'Texas'
}
]
function sortAndPrint( obj, field ) {
console.log(`Sorting by ${field}`);
console.log(obj.sort((a, b) => a[field] > b[field] ) );
}
sortAndPrint(obj, 'name');
sortAndPrint(obj, 'address');
sortAndPrint(obj, 'age');
Entry with missing keys will be on the bottom
Ask for the current value to decide what will be compared or what will be at the bottom.
let obj=[{name:'aa',age:'2y',address:'Alaska'},{name:'cc',age:'4y',address:'California'},{name:'bb',address:'Texas'}],
field = 'age';
console.log(obj.sort((a, b) => a[field] ? b[field] ? a[field].toString().localeCompare(b[field].toString()) : -1 : 1));
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you want to compare the numbers within this string 10years or this string 5y, and so on, use a regex to compare the numbers.
let obj=[{name:'aa',age:'24y',address:'Alaska'},{name:'cc',age:'4years',address:'California'},{name:'bb',address:'Texas'}],
field = 'age';
console.log(obj.sort((a, b) => {
let evaluate = () => {
let aval = a[field].replace(/[^\d]/g, '').trim();
let bval = b[field].replace(/[^\d]/g, '').trim();
return aval !== '' && bval !== '' ? Number(aval) - Number(bval) : a[field].toString().localeCompare(b[field].toString());
};
return a[field] ? b[field] ? evaluate() : -1 : 1
}));
.as-console-wrapper { max-height: 100% !important; top: 0; }
let obj=[
{name:'aa',age:'25',address:'Alaska'},
{name:'cc',age:'25',address:'California'},
{name:'bb',address:'Texas'}
]
let field='age'; //work for name and address
const sortFunc = (a, b) => a[field].toString().localeCompare(b[field].toString())
// If you can discard entries without the field
console.log(obj.filter(e => field in e).sort(sortFunc))
// If you cannot
console.log(obj.filter(e => field in e).sort(sortFunc).concat(obj.filter(e => !(field in e))))
This happens because on the last element you don't have the property age when you're trying to access it with property toString.(it is null with the age key)
Just in case you're looking for an overkill solution :)
let obj=[
{name: 'aa', age: 25, address: 'Alaska'},
{name: 'cc', age: 24, address: 'California'},
{name: 'mm', address: 'Texas'}
];
let field = 'age';
console.log (obj.sort((a, b) => {
a = a[field];
b = b[field];
let defaultValue = '';
if (typeof a === 'number' || typeof b === 'number') {
defaultValue = 0;
}
a = a || defaultValue;
b = b || defaultValue;
if (typeof a === 'number') {
return a - b;
}
else {
return a.localeCompare(b);
}
}));
This automatically handles either strings or numbers, sorting correctly for each. If you want the no-age entries to sort higher rather than lower than everything else, just change what defaultValue is set to for numbers to a large number.

How to group objects with specific key in javaScript

I want to do a grouping for JavaScript object as age.My JSON is
var jsonObj=[{"name":"john","age":23},{"name":"mark","age":25},{"name":"jeni","age":21}]`
I want to be the group result like here.
[23:{"name":"john","age":23},25:{"name":"mark","age":25},21:{"name":"jeni","age":21}]
Please help me to get a result, I try with map and filter but did not get the result.
Use Array#reduce to group the objects. Because there can multiple people with the same age, collect them into an array under the age property:
var jsonObj=[{"name":"john","age":23},{"name":"mark","age":25},{"name":"poll","age":23},{"name":"jeni","age":21}];
var result = jsonObj.reduce(function(r, o) {
r[o.age] || (r[o.age] = []); // if the age key doesn't exist, set it to be an array
r[o.age].push(o); // push the object into the array
return r;
}, {});
console.log(result);
Or the fancy ES6 one liner version:
var jsonObj=[{"name":"john","age":23},{"name":"mark","age":25},{"name":"poll","age":23},{"name":"jeni","age":21}];
var result = jsonObj.reduce((r, o) => ((r[o.age] || (r[o.age] = [])).push(o), r), {});
console.log(result);
You could take the hash table as result. Then loop and create new arrays for new keys. Later push the object.
var data = [{ name: "john", age: 23 }, { name: "mark", age: 25 }, { name: "poll", age: 23 }, { name: "jeni", age: 21 }],
result = {};
data.forEach(o => (result[o.age] = result[o.age] || []).push(o));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use underscore js to solve this problem.Add underscore js first<script src='https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js' ></script> then you can simply get the result.
var result=_.indexBy(jsonObj, 'age');
You can try it :
var jsonObj = [{ "name": "john", "age": 23 }, { "name": "mark", "age": 25 }, { "name": "poll", "age": 23 }, { "name": "jeni", "age": 21 }];
let result = {};
jsonObj.forEach(item => {
if(result.hasOwnProperty(item.age)) {
let arr = [];
if(Array.isArray(result[item.age])) {
result[item.age].push(item);
} else {
result[item.age] = [result[item.age], item];
}
} else {
result[item.age] = item;
}
});
console.log(result)

Fill in missing properties in an array of objects

What is the best way to fill in missing properties in an array of objects, such as this example:
[
{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com'
},
{
name: 'Richard',
number '07777 666 555'
},
{
name: 'Harry',
website: 'http://www.harry.com'
}
]
I need to add the missing properties with a null value, so that when I pass this array on to be rendered in something such as a HTML table or CSV file, everything lines up correctly. I was thinking of passing over the array twice, once to get all the possible properties, and a second time to add those missing properties with a null value to each object where it doesn't exist. Is there a better way to do this?
EDIT: I won't know what the keys are until I have the data, it's coming from an API and the keys are not always requested explicitly.
My final solution
Thanks all, it seems the two pass approach is indeed the best approach. After I started to write this using the examples provided, I realised that the order of the properties wasn't being maintained. This is how I achieved filling in the missing props, and maintaining the correct order. Any suggestions for potential improvements are welcome.
var fillMissingProps = function(arr) {
// build a list of keys in the correct order
var keys = [];
arr.forEach(function(obj) {
var lastIndex = -1;
Object.keys(obj).forEach(function(key, i) {
if (keys.includes(key)) {
// record the position of the existing key
lastIndex = keys.lastIndexOf(key);
if (lastIndex < i) {
// this key is in the wrong position so move it
keys.splice(i, 0, keys.splice(lastIndex, 1)[0]);
lastIndex = i;
}
} else {
// add the new key in the correct position
// after the previous existing key
lastIndex++;
keys.splice(lastIndex, 0, key);
}
});
});
// build a template object with all props set to null
// and in the correct position
var defaults = {};
keys.forEach(function(key) {
defaults[key] = null;
});
// and update the array by overwriting each element with a
// new object that's built from the template and the original object
arr.forEach(function(obj, i, arr) {
arr[i] = Object.assign({}, defaults, obj);
});
return arr;
};
/** TEST **/
var currentArray = [
{
website: 'http://www.unknown.com'
},
{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com'
},
{
title: 'Mr',
name: 'Richard',
gender: 'Male',
number: '04321 666 555'
},
{
id: '003ABCDEFGHIJKL',
name: 'Harry',
website: 'http://www.harry.com',
mobile: '07890 123 456',
city: 'Brentwood',
county: 'Essex'
}
];
var newArray = fillMissingProps(currentArray);
for (var i = 0; i < newArray.length; i++) {
for (var prop in newArray[i]) {
console.log(prop + ": " + newArray[i][prop]);
}
console.log('---------');
}
Given that you don't know apriori which keys are supposed to exist, you have no choice but to iterate over the array twice:
// build a map of unique keys (with null values)
var keys = {}
array.forEach(el => Object.keys(el).forEach(k => keys[k] = null));
// and update the array by overwriting each element with a
// new object that's built from the null map and the original object
array.forEach((el, ix, a) => a[ix] = Object.assign({}, keys, el));
Use Array.prototype.map():
const arr = [
{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com',
},
{
name: 'Richard',
number: '07777 666 555',
},
{
name: 'Harry',
website: 'http://www.harry.com',
},
];
const newArr = arr.map(x => (
arr.map(x => Object.keys(x))
.reduce((a, b) =>
(b.forEach(z => a.includes(z) || a.push(z)), a)
)
.forEach(
y => (x[y] = x.hasOwnProperty(y) ? x[y] : null)
), x)
);
console.log(newArr);
Here is a more interesting answer, its a tad fun one but it will build up your objects on the fly as new properties appear:
var currentArray = [
{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com'
},
{
name: 'Richard',
number: '07777 666 555'
},
{
name: 'Harry',
website: 'http://www.harry.com'
}
]
var newArray = []
function NewObject() {
}
for(var i = 0; i < currentArray.length; i++){
var nObj = new NewObject();
for(var prop in currentArray[i]){
if(!NewObject.hasOwnProperty(prop))
NewObject.prototype[prop] = null;
nObj[prop]=currentArray[i][prop];
}
newArray.push(nObj);
}
for(var i = 0; i < newArray.length; i++){
for(var prop in newArray[i]){
console.log(prop+ ": "+newArray[i][prop]);
}
console.log('---------');
}
It builds new objects from the ones you provide and adds new properties to the objects if they don't exist already.
This idea was more for curiosities sake tho so any comments would be interesting :)
You can get all keys and set all keys using for..of loop, .map() to iterate all Object.keys(), redefine original array
var arr = [{
name: 'Harry',
website: 'http://www.harry.com'
},{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com'
}, {
name: 'Richard',
number: '07777 666 555'
}];
for (var obj of arr) {
for (var key of Object.keys(obj)) {
arr = arr.map(o => (o[key] = o[key] || null, o))
}
};
console.log(arr);
Something like this could work:
for (var i = 0; i < arrayLength; i++) {
yourArray[i].name = yourArray[i].name || null;
yourArray[i].number = yourArray[i].number || null;
yourArray[i].website= yourArray[i].website|| null;
}

How to get Property value from a Javascript object

I have a JavaScript object.
var obj = { Id: "100", Name: "John", Address: {Id:1,Name:"Bangalore"} }
var dataToRetrieve= "Name";
function GetPropertyValue(object,dataToRetrieve){
return obj[dataToRetrieve]
}
var retval = GetPropertyValue(obj,dataToRetrieve)
This works fine. But if I try to get the value of property value of "Address.Name" ,
Like : var dataToRetrieve = "Address.Name";
it shows undefined.
Note : The property variable is set by user from HTML And it can be changed according to user requirement(which property value he wants).
What I want to achieve :
1) If dataToRetrieve = "Name" , it should give me "John",
2) If dataToRetrieve = "Id" , it should give me "100",
3) If dataToRetrieve = "Address.Name" , it should give me "Bangalore",
4) If dataToRetrieve = "Address.Id" , it should give me 1
Plunkr Here : PLUNKR
Use reduce() method
var obj = {
Id: "100",
Name: "John",
Address: {
Id: 1,
Name: "Bangalore"
}
}
function GetPropertyValue(obj1, dataToRetrieve) {
return dataToRetrieve
.split('.') // split string based on `.`
.reduce(function(o, k) {
return o && o[k]; // get inner property if `o` is defined else get `o` and return
}, obj1) // set initial value as object
}
console.log(
GetPropertyValue(obj, "Name"),
GetPropertyValue(obj, "Id"),
GetPropertyValue(obj, "Address.Name"),
GetPropertyValue(obj, "Address.Id"),
GetPropertyValue(obj, "Address.Idsd"),
GetPropertyValue(obj, "Addre.Idsd")
)
For older browser check polyfill option of reduce method.
Use following function:
var obj = { Id: "100", Name: "John",
Address: [{ Id:1, Name:"Bangalore" }, { Id:2, Name: "Mysore" } ] };
function GetPropertyValue(object, dataToRetrieve) {
dataToRetrieve.split('.').forEach(function(token) {
if (object) object = object[token];
});
return object;
}
console.log(
GetPropertyValue(obj, "Address.0.Name"),
GetPropertyValue(obj, "Address.1.Id"),
GetPropertyValue(obj, "Name"),
GetPropertyValue(obj, "Id"),
GetPropertyValue(obj, "Unknown"),
GetPropertyValue(obj, "Some.Unknown.Property")
);
function GetPropertyValue(object,dataToRetrieve){
var valueArray = dataToRetrieve.split(".");
if (valueArray.length <= 1) {
return object[valueArray];
} else {
var res;
function browseObj(obj, valueArray, i) {
if (i == valueArray.length)
res = obj;
else
browseObj(obj[valueArray[i]], valueArray, i+1);
}
browseObj(object, valueArray, 0);
return res;
}
}
I had written a standard reusable Object method to access nested properties dynamically. It's like
Object.prototype.getNestedValue = function(...a) {
return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};
It will take dynamic arguments for the nested properties. If they are string type they are object properties if number type then they are array indices. Once you have this, your job becomes very easy. Let's see..
Object.prototype.getNestedValue = function(...a) {
return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};
var props = ["Address","Name"],
obj = { Id: "100", Name: "John", Address: {Id:1,Name:"Bangalore"} },
val = obj.getNestedValue(...props);
console.log(val);
// or you can of course do statically like
val = obj.getNestedValue("Address","Name");
console.log(val);
You can see getNestedValue() and it's twin setNestedValue() working at https://stackoverflow.com/a/37331868/4543207

Categories