Extract only properties that have value in object - javascript

I have an object that looks like this for example:
let data = {
color: "green",
color1: "red",
color2: null,
color3: 34,
color4: null,
color5: true,
color6: [],
}
I want to extract only the properties that have value and make a new object. So this new object would look like this:
let data = {
color: "green",
color1: "red",
color3: 34,
color5: true,
}
This would be dynamic, properties and values can change but at the end I always wanna make an object that has just values. What would be some practical way to do it?

You can create a new object by following the steps mentioned below :
You can iterate through the keys using Object.keys
Then, you can use Array.reduce to convert the array of keys into the transformed object.
In acc, you will get the accumulated object.
In curr, you will receive the current key
Create a function isEmptyObject where you can define rules to check if present or not. As per your example, I have checked for null and Array length. You can modify this function.
let data = {
color: "green",
color1: "red",
color2: null,
color3: 34,
color4: null,
color5: true,
color6: [],
}
var newObj = Object.keys(data).reduce((acc, curr) => {
if (isEmptyObject(data, curr)) return acc;
acc[curr] = data[curr];
return acc;
}, {});
function isEmptyObject(obj, key) {
return obj[key] === null || Array.isArray(obj[key]) && obj[key].length === 0;
}
console.log(newObj);

Related

Best way to re arrange array of object?

I have an array or object which will serve as columns for a table with a unique key
const list = [{
key: "Name",
textColor: "red"
},{
key: "Age",
textColor: "green"
},{
key: "Occupation",
textColor: "yellow"
}]
And, I have a list of ordering of columns in the table
const newOrder = ["Occupation", "Name", "Age"]
Now , how can i rearrange the list according to the newOrder without using nested loops. Also, these all are dyanamic, so its not just about the above mentioned three columns
Expected Output
const list = [{
key: "Occupation",
textColor: "yellow"
},{
key: "Name",
textColor: "red"
},{
key: "Age",
textColor: "green"
}]
Your list can be reformatted to a regular javascript object in which key is the property name, and textColor is the value:
const toObject = kvps => Object.fromEntries(kvps.map(kvp => [ kvp.key, kvp.textColor ]));
With a given array of keys, you can pick values from that object like so:
const fromObject = (order, obj) => order.map(key => ({ key, textColor: obj[key] }));
Chain the two together, and you can reorder any list of key value pairs:
const list = [{
key: "Name",
textColor: "red"
},{
key: "Age",
textColor: "green"
},{
key: "Occupation",
textColor: "yellow"
}]
const toObject = kvps => Object.fromEntries(kvps.map(kvp => [ kvp.key, kvp.textColor ]));
const fromObject = (order, obj) => order.map(key => ({ key, textColor: obj[key] }));
const reorder = (order, kvps) => fromObject(order, toObject(kvps));
const newList = reorder(["Occupation", "Name", "Age"], list);
console.log(
newList
)
Edit: if the sizes of your list and order arrays are small, you probably want to go with the much easier to read approach suggested by Jon Webb in one of the other answers. 🙂 I tried to keep my solution to an O(n + m) complexity rather than O(n * m), (n = list size, m = order size) but it's probably not worth the added complexity.
You can iterate on the "order" list, finding the corresponding item in the original list, and push the item to the new list in that order:
const orderList = (list, order) => {
const newList = [];
for (key of order) {
const item = list.find((obj) => obj.key == key);
if (item) newList.push(item);
}
return newList;
}
You can use the sort method on the array. The sort method will sort in place so you should copy your array if you dont want to mutate the original.
The array sort method takes a compare function the receives two elements for comparison a and b. It should return a number and it will sort them depending on that number:
If > 0 then b is before a
If < 0 then b is after a
If 0 then keep as is
By using indexOf on the newOrder array can get the index of the key. And index of 0 should come before and index of 1 should come before and index of 2 of course. So if the index of a.key is 2 and the index of b.key is 0, then we should return a value greater than 0 since b should come before a.
In my implementation below I'm cloning the original list ([...list]) as to not mutate accidentally. You could just as well do list.sort(...) if you don't need or care about mutating.
const list = [{
key: "Name",
textColor: "red"
},{
key: "Age",
textColor: "green"
},{
key: "Occupation",
textColor: "yellow"
}]
const newOrder = ["Occupation", "Name", "Age"]
function sort(list, order) {
return [...list].sort((a, b) => {
const keyIndexA = order.indexOf(a.key);
const keyIndexB = order.indexOf(b.key);
if (keyIndexA < keyIndexB) return -1;
if (keyIndexA > keyIndexB) return 1;
return 0;
});
}
console.log(sort(list, newOrder));
You can use just regular sort
const list = [{key: "Name",textColor: "red"},{key: "Age",textColor: "green"},{key: "Occupation",textColor: "yellow"}];
const newOrder = ["Occupation", "Name", "Age"];
const result = list.sort(({key: a}, {key: b}) => newOrder.indexOf(a) - newOrder.indexOf(b));
console.log(result);
.as-console-wrapper{min-height: 100%!important; top: 0}
You can try to loop through the newOrder array, find the object that correlates to the first item and push to a new array.
const orderedList = [];
newOrder.forEach(order => {
orderedList.push(list.find(({key}) => key === order));
})
You can use orderBy from lodash library

JS change nested property

I need to change a nested variable property. First check if it exists then change it to 'N/A' if necessary. This is what I have.
const json = {
isCat: {
isAvaliable: true,
count: 5
},
isDog: {
isAvaliable: true,
count: 10
},
isFrog: {
isAvaliable: false,
count: null
}
}
const newJson = { ...json }
if(!jsonObj.isCat.count) {
newJson = {...newJson, json.isCat.count: 'N/A'}
}
Im not sure how to set count lets say by goign directly to it and changing the value. It seems simple maybe im missing something.
I can see the value in the if statement but i cant make any changes to the actual property value itself. Basically if a value is null, change it to 'N/A'
Using Object.entries and Object.fromEntries you can map the old object to the new object. If the count property is truthy, i.e. not null then map it through, otherwise shallow copy the element and update the count property with the new "N/A" value. This avoids mutating your original object.
const newJson = Object.fromEntries(Object.entries(json).map(([key, value]) => ([
key,
value.count ? value : { ...value,
count: 'N/A'
}
])));
const json = {
isCat: {
isAvaliable: true,
count: 5
},
isDog: {
isAvaliable: true,
count: 10
},
isFrog: {
isAvaliable: false,
count: null
}
};
const newJson = Object.fromEntries(Object.entries(json).map(([key, value]) => ([
key,
value.count ? value : { ...value,
count: 'N/A'
}
])));
console.log(newJson);
You can use array reduce method. First get all the keys using Object.keys method. Then traverse the keys and check the count is null or not. If null then change it to 'N/A'.
const json = {
isCat: {
isAvaliable: true,
count: 5,
},
isDog: {
isAvaliable: true,
count: 10,
},
isFrog: {
isAvaliable: false,
count: null,
},
};
const ret = Object.keys(json).reduce((prev, c) => {
const p = prev;
if (!json[c].count) p[c] = { ...json[c], count: 'N/A' };
else p[c] = { ...json[c] };
return p;
}, {});
console.log(ret);
Another solution with reduce. Beware of it's comma operator in the return value.
const json = {isCat: {isAvaliable: true,count: 5,},isDog: {isAvaliable:true,count: 10,},isFrog: {isAvaliable: false,count: null,},};
const res = Object.keys(json).reduce((pV,cV)=>(json[cV].count==null?json[cV].count='N/A':null,json),{});
console.log(res);

Use array.some to return only matching sub-objects

I have a list of requested colours that I am passing to a function:
const requestedColours = ['blue','green'];
I pass this into a function which contains an object with details of various colours:
colorOptions(requestedColours) {
const options = {
blue: {
icon: 'sea.jpg',
title: 'Sea Blue',
},
green: {
icon: 'leaf.jpg',
title: 'Leafy green',
},
pink: {
icon: 'flower.jpg',
title: 'Rose Pink',
}
}
return options.some(requestedColours);
}
The following line I'm aware is incorrect:
return options.some(requestedColours);
However I also tried, for example:
return options.some('blue');
And this does not return the sub-object for blue, how do I correctly use array.some?
Array#reduce over requestedColours array.
Note: A wise idea would be also moving the options object outside the function.
const requestedColours = ['blue', 'green'];
const options = {
blue: {
icon: 'sea.jpg',
title: 'Sea Blue',
},
green: {
icon: 'leaf.jpg',
title: 'Leafy green',
},
pink: {
icon: 'flower.jpg',
title: 'Rose Pink',
}
};
const colorOptions = (obj, arr) => {
return requestedColours.reduce((s, a) => {
if (a in options) {
s[a] = obj[a];
}
return s;
}, {});
};
console.log(colorOptions(options, requestedColours));
The some function takes a function and uses it to test each element of the array. It returns true if the function returns true for any element of the array. For example, if I wanted to check if an array contained at least one element that was greater than 5, I might try:
[1, 2, 5, 7].some(function(elem) { return elem > 5; })
I'm not too sure what you're trying to achieve in the above function, but I don't think the some function meets your needs.
some is a method in the array prototype that takes a function and returns either true or false. It runs the function with the items in the array until it returns a truthy value. If none appears, it returns false.
If your goal is to see if the requestedColors are in options:
define a function that tells you if a key is in an object
pass this function to some
i.e.:
var requestedColours=["blue","green"];
var options={blue:{icon:"sea.jpg",title:"Sea Blue"},green:{icon:"leaf.jpg",title:"Leafy green"},pink:{icon:"flower.jpg",title:"Rose Pink"}};
var objectHasKey = obj => key => key in obj;
console.log(
requestedColours.some(objectHasKey(options))
);
If your goal is to create an array of requested options:
define a function that retrieves a property's value from an object
use map to map from key to option
filter out options that don't exist
var requestedColours=["red", "blue","green"];
var options={blue:{icon:"sea.jpg",title:"Sea Blue"},green:{icon:"leaf.jpg",title:"Leafy green"},pink:{icon:"flower.jpg",title:"Rose Pink"}};
var getKey = obj => key => obj[key];
console.log(
requestedColours
.map(getKey(options))
.filter(opt => opt)
);
If your goal is to create a new options object with only the requested colours:
You can do this in one loop, using one reduce method:
Reduce requestedColours
Retrieve the options object from options using the key
Merge it with the accumulator you pass along in the reducer
const requestedColours = ['blue', 'green'];
const options = {
blue: {
icon: 'sea.jpg',
title: 'Sea Blue',
},
green: {
icon: 'leaf.jpg',
title: 'Leafy green',
},
pink: {
icon: 'flower.jpg',
title: 'Rose Pink',
}
}
const pickedColors = requestedColours.reduce(
(picked, key) => Object.assign(picked, { [key]: options[key] }),
{}
);
console.log(pickedColors);

Check if an array contains a specified object

The following function searches an object recursively through an object that has nested arrays:
function findDeep(arr, obj) {
console.log(arr)
if (arr.indexOf(obj) !== -1) {
console.log(arr)
return arr
} else {
arr.forEach(item => {
if (item.children) findDeep(item.children, obj)
})
}
}
const colors = {
children: [
{
name: 'white',
},
{
name: 'yellow',
children: [
{
name: 'black'
}
]
}
]
}
const color = {
name: 'black'
}
findDeep(colors.children, color)
The first console.log(arr) do log the matched array:
[
{ name: 'black' }
]
But he second console.log(arr) doesn't log anything. Shouldn't arr.indexOf(obj) return 1, and therefore make the second console.log(arr) log the array?
Here's the CodePen.
You can not find index of object in array using indexOf unless both the objects(passed in indexOf to test and present in array) are pointing to the same reference.
For example:
var a = {
a: 10
};
var b = [{
a: 10
}, {
b: 20
}];
console.log(b.indexOf(a)); // Object `a` and Object in `0th` index of the array are having similar `key-values`
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
But,
var a = {
a: 10
};
var b = [a, {
b: 20
}];
//`0th` index in the array is nothing but a variable holding `object`
console.log(b.indexOf(a)); //Same variable is tested in `indexOf`
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
From the docs, indexOf() compares searchElement to elements of the Array using strict equality (the same method used by the === or triple-equals operator).
{} === {} will be evaluated as false because,
An expression comparing Objects is only true if the operands reference the same Object. If both operands are objects, then JavaScript compares internal references which are equal when operands refer to the same object in memory.[Ref]
There are few solutions and approaches but all of them will be doing iteration and comparing value of the key in object. Refer this answer

Changing the order of the Object keys....

var addObjectResponse = [{
'DateTimeTaken': '/Date(1301494335000-0400)/',
'Weight': 100909.090909091,
'Height': 182.88,
'SPO2': '222.00000',
'BloodPressureSystolic': 120,
'BloodPressureDiastolic': 80,
'BloodPressurePosition': 'Standing',
'VitalSite': 'Popliteal',
'Laterality': 'Right',
'CuffSize': 'XL',
'HeartRate': 111,
'HeartRateRegularity': 'Regular',
'Resprate': 111,
'Temperature': 36.6666666666667,
'TemperatureMethod': 'Oral',
'HeadCircumference': '',
}];
This is a sample object which i am getting from back end, now i want to change the order of the object. I don't want to sort by name or size... i just want to manually change the order...
If you create a new object from the first object (as the current accepted answer suggests) you will always need to know all of the properties in your object (a maintenance nightmare).
Use Object.assign() instead.
*This works in modern browsers -- not in IE or Edge <12.
const addObjectResponse = {
'DateTimeTaken': '/Date(1301494335000-0400)/',
'Weight': 100909.090909091,
'Height': 182.88,
'SPO2': '222.00000',
'BloodPressureSystolic': 120,
'BloodPressureDiastolic': 80,
'BloodPressurePosition': 'Standing',
'VitalSite': 'Popliteal',
'Laterality': 'Right',
'CuffSize': 'XL',
'HeartRate': 111, // <-----
'HeartRateRegularity': 'Regular', // <-----
'Resprate': 111,
'Temperature': 36.6666666666667,
'TemperatureMethod': 'Oral',
'HeadCircumference': '',
};
// Create an object which will serve as the order template
const objectOrder = {
'HeartRate': null,
'HeartRateRegularity': null,
}
const addObjectResource = Object.assign(objectOrder, addObjectResponse);
The two items you wanted to be ordered are in order, and the remaining properties are below them.
Now your object will look like this:
{
'HeartRate': 111, // <-----
'HeartRateRegularity': 'Regular', // <-----
'DateTimeTaken': '/Date(1301494335000-0400)/',
'Weight': 100909.090909091,
'Height': 182.88,
'SPO2': '222.00000',
'BloodPressureSystolic': 120,
'BloodPressureDiastolic': 80,
'BloodPressurePosition': 'Standing',
'VitalSite': 'Popliteal',
'Laterality': 'Right',
'CuffSize': 'XL',
'Resprate': 111,
'Temperature': 36.6666666666667,
'TemperatureMethod': 'Oral',
'HeadCircumference': '',
}
I wrote this small algorithm which allows to move keys, it's like jQuery .insertAfter() method. You have to provide:
//currentKey: the key you want to move
//afterKey: position to move-after the currentKey, null or '' if it must be in position [0]
//obj: object
function moveObjectElement(currentKey, afterKey, obj) {
var result = {};
var val = obj[currentKey];
delete obj[currentKey];
var next = -1;
var i = 0;
if(typeof afterKey == 'undefined' || afterKey == null) afterKey = '';
$.each(obj, function(k, v) {
if((afterKey == '' && i == 0) || next == 1) {
result[currentKey] = val;
next = 0;
}
if(k == afterKey) { next = 1; }
result[k] = v;
++i;
});
if(next == 1) {
result[currentKey] = val;
}
if(next !== -1) return result; else return obj;
}
Example:
var el = {a: 1, b: 3, c:8, d:2 }
el = moveObjectElement('d', '', el); // {d,a,b,c}
el = moveObjectElement('b', 'd', el); // {d,b,a,c}
You can't order JavaScript object key/value pairs. It's stored in its own internal format, so you should never rely on the order of that. In JS, everything is an Object, even an Array. So sometimes you can introduce bugs when using array notation and object notation together (for x in var)
I like the approved answer by Chamika Sandamal. Here's a simple function that uses their same logic with a little be of freedom to change the order as you need it.
function preferredOrder(obj, order) {
var newObject = {};
for(var i = 0; i < order.length; i++) {
if(obj.hasOwnProperty(order[i])) {
newObject[order[i]] = obj[order[i]];
}
}
return newObject;
}
You give it an object, and an array of the key names you want, and returns a new object of those properties arranged in that order.
var data = {
c: 50,
a: 25,
d: 10,
b: 30
};
data = preferredOrder(data, [
"a",
"b",
"c",
"d"
]);
console.log(data);
/*
data = {
a: 25,
b: 30,
c: 50,
d: 10
}
*/
I'm copying and pasting from a big JSON object into a CMS and a little bit of re-organizing of the source JSON into the same order as the fields in the CMS has saved my sanity.
2020 Update
The answer below was correct at time of writing in 2011. However, since ES6, enumeration order has been specified as part of the language. Here's a nice article summarising this: https://2ality.com/2015/10/property-traversal-order-es6.html
Original answer
Properties of an object in JavaScript do not have an order. There may appear to be an order in some browsers but the ECMAScript specification defines object property enumeration order as being implementation-specific so you should not assume one browser's behaviour will be the same as another's. Chrome, for example, does not use the same ordering as some other browsers: see this lengthy bug report for at least as much discussion of this issue as you could possibly want.
If you need a specific order, use an array, or two arrays (one for keys and one for values).
You can use Object.keys():
var obj = {
a: 1,
b: 2,
c: 4
};
var new_obj = {};
Object.keys(obj)
.sort(function(a, b) {
/** Insert your custom sorting function here */
return a - b;
})
.forEach(function(key) {
new_obj[key] = obj[key];
});
obj = new_obj;
if you want to manually reorder. simply create new object and assign values using old object.
var newObject= [{
'DateTimeTaken': addObjectResponse.DateTimeTaken,
'Weight': addObjectResponse.Weight,
'Height': addObjectResponse.Height,
'SPO2': addObjectResponse.SPO2
}];
If you do not want to create a new object, you can use the following code snippet.
function orderKey(obj, keyOrder) {
keyOrder.forEach((k) => {
const v = obj[k]
delete obj[k]
obj[k] = v
})
}
here is a codepen: https://codepen.io/zhangyanwei/pen/QJeRxB
I think that's not possible in JavaScript.
You can create an array which will contain the field names in your order and you can iterate through this array and fetch the fields from the actual object.
Just refer to the object keys in the order that you like:
aKeys = [
addObjectResponse[0].DateTimeTaken,
addObjectResponse[0].Weight,
addObjectResponse[0].Height,
...etc...
]
I wrote a quick function in TypeScript that takes 2 arguments. The first is the array of objects you want to change the keys of, the second is an array of strings that represent the order of keys you'd like returned.
type GenericObject = Record<string, any> | null;
const order:Function = (data: Array<GenericObject>, order: Array<string>): Array<GenericObject> => {
return data.map((node) => {
return order.reduce((runningValue, currentValue) => {
return Object.assign(runningValue, { [currentValue]: node?.[currentValue]});
}, {});
});
};
And here is an example of calling it:
const data: Array<GenericObject> = [
{ b: 1, a: 2},
{ b: 3, a: 4},
];
const orderIWant: Array<string> = ['a', 'b'];
const ordered: Array<GenericObject> = order(data, orderIWant);
console.log(ordered);
// [ { a: 2, b: 1 }, { a: 4, b: 3 } ]
function orderKeys(obj, keys){
const newObj = {};
for(let key of keys){
newObj[key] = obj[key];
}
return newObj;
}

Categories