Copy properties of object to another skipping undefined ones - javascript

How can I copy a list of properties from a object to another skipping the null or undefined values?
Something like this
const obj1 = { prop1: 'value', prop2: undefined }
const obj2 = copy(obj1, ['prop1', 'prop2', 'prop3']) // prop3 doesn't exist and prop2 should be excluded
console.log(obj2) // should print { prop1: 'value' }

Here using Object.fromEntries() and Array.prototype.filter().
see: Object.fromEntries(): Object transformations
const obj1 = { prop1: 'value', prop2: undefined }
const copy = (obj, props) => {
return Object.fromEntries(Object.entries(obj)
.filter(([k, v]) => (
(props.includes(k) && v !== undefined && v !== null)
)));
}
const obj2 = copy(obj1, ['prop1', 'prop2', 'prop3'])
console.log(obj2)

Try this:
const obj1 = { prop1: 'value', prop2: undefined }
function copy(obj1, props)
{
return props.reduce(
(acum, current) =>
(obj1[current] !== undefined && obj1[current] !== null) ?
({...acum, [current]:obj1[current]}):
acum
,{}
)
}
const obj2 = copy(obj1, ['prop1', 'prop2', 'prop3']) // prop3 doesn't exist and prop2 should be excluded
console.log(obj2) // should print { prop1: 'value' }

function copy(object, keys) {
const clone = {};
for(const key of keys) {
if(!isNaN(object[key])) clone[key] = object[key];
}
return clone;
}

It's not clear what the purpose of the array is in your code, but this should work for the problem as you stated it. It reduces over the entries of the original object with a new object as an accumulator, and only copies over properties with defined values. To include null in the check, you can change the condition to be != null (because undefined == null).
const copyExcludingUndefined = (obj = {}) =>
Object.entries(obj)
.reduce((a, [k, v]) => {
if (v !== undefined) {
a[k] = v
}
return a
}, {})

Write your own copy function :)
let copy = function(o, args) {
let obj = {};
if (Array.isArray(args)) {
args.forEach(arg => {
if(o[arg] !== undefined) {
obj[arg] = o[arg];
}
});
}
return obj;
}

Related

Find if value is undefined of multiple keys in object

I would like to detect if some values are not defined in an object with several properties
for example :
let test = {
helo: undefined,
hey: "not undefined"
}
i tried this :
const object1 = {
a: 'somestring',
b: 42
};
for (const [key, value] of Object.entries(object1)) {
console.log(`${key}: ${value}`);
}
but if possible, I don't want to use a for loop, I would like a boolean result in return
You could be looking for something like this.
const test = {
helo: undefined,
hey: "not undefined"
};
const some_undefined = Object.values(test).some(v => v === undefined);
console.log(some_undefined);
You can use a function for that :
const hasOneKeyUndefined = (object)=>
{
for (const [key, value] of Object.entries(object)) {
if (value === undefined) return true;
}
return false;
}
You could also use Object.keys
const test = {
helo: undefined,
hey: "not undefined"
};
Object.keys(test).map((key, idx) => test[key] === undefined)

How to get values from array of objects in javascript

I have an array of object from which I am trying to get values using map operator but I am getting the whole json objects all I want is just array of values.
Below is my code:
const obj = [
{
a: {
b: 'Paul',
}
},
{
c: 'Byeeee',
}
];
obj.map((val) => console.log(val));
what I am getting is
{ a: { b: 'Paul' } }
{ c: 'Byeeee' }
What I want is:
['Paul','Byeeee']
Someone let me know how can I get the desired output.
You can do this recursively. You can first start off by grabbing the values of your object, and then loop through those using .flatMap(). If you encounter a value that is an object, you can recursively grab the values of that object by recalling your function. Otherwise, you can return the value. The advantage of using .flatMap() here is that when the recursive call returns an array, we don't end up with inner arrays, but rather the array gets flattened into one resulting array:
const obj = [{ a: { b: 'Paul', } }, { c: 'Byeeee', } ];
const getValues = (obj) => {
return Object.values(obj).flatMap(val => Object(val) === val ? getValues(val) : val);
}
console.log(getValues(obj));
you can use the following solution.
const data = [{ a: { b: 'Paul' } }, { c: 'Byeeee' }];
const flatObjectValues = (obj, result) => {
// recursive function to get object values
const objValues = Object.values(obj);
if (objValues?.length > 0) {
objValues.map((v) => {
if (typeof v === 'object' && !Array.isArray(v)) {
flatObjectValues(v, result);
} else {
result.push(v);
}
return v;
});
}
};
const updatedData = [];
data.map((x) => flatObjectValues(x, updatedData));
console.log('updatedData: ', updatedData);
You can use recursion with array.reduce, like fellowing.
function getAllValues(objuct) {
return objuct.reduce((acc, curr) => {
if (typeof curr === 'object') {
return [...acc, ...getAllValues(Object.values(curr))];
}
return [...acc, curr];
}, []);
}
A recursive solution could be:
const arr = [{a: {b: "Paul",},},{c: "Byeeee",},];
const flatArrOfObjects = (arr) => {
const values = [];
for (const i in arr) flatObj(arr[i], values);
return values;
};
const flatObj = (obj, result) => {
for (const [key, value] of Object.entries(obj)) {
if (typeof value === "object") flatObj(value, result);
else result.push(value);
}
};
console.log(flatArrOfObjects(arr));

Why my recursive function works only for one level?

I am trying to learn how to cope with the objects and arrays and I saw many ways of iterating objects but recursing doesn't work for me and I don't understand why. What am I doing wrong?
I need to loop through an object and just slightly change something in an array. In my case, it's uppercasing the keys
Here is what I've got for now
const development = {
port: 8080,
db: {
username: "jkdfs",
password: "dsfsdg",
name: "kslfjskd",
test: { test: 12, another: 'value' }
},
token_secret: "jfkjdsj",
hash_rounds: "kjsfkljdfkl"
};
function throughObject(obj) {
let collection = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key];
if (typeof obj[key] !== 'object') {
collection[key.toUpperCase()] = value;
} else {
collection[key.toUpperCase()] = nestedObject(obj[key]);
}
}
function nestedObject(nested) {
const sub = {};
for (const k in nested) {
let v = nested[k];
if (typeof nested[k] !== 'object') {
sub[k.toUpperCase()] = v;
} else {
nestedObject(v);
}
}
return sub;
}
}
return collection;
}
const r = throughObject(development);
console.log(r);
When you're recursively calling the function on an object value, you still need to assign it to the sub object: sub[k.toUpperCase()] = nestedObject(v). Also, you don't need 2 different functions.
const development = {
port: 8080,
db: {
username: "jkdfs",
password: "dsfsdg",
name: "kslfjskd",
test: { test: 12, another: 'value' }
},
token_secret: "jfkjdsj",
hash_rounds: "kjsfkljdfkl"
};
function nestedObject(nested) {
const sub = {};
for (const k in nested) {
const v = nested[k];
if (typeof nested[k] !== 'object')
sub[k.toUpperCase()] = v;
else
sub[k.toUpperCase()] = nestedObject(v); // <- HERE
}
return sub;
}
console.log(nestedObject(development))
Here's a shorter version using Object.fromEntries()
const development={port:8080,db:{username:"jkdfs",password:"dsfsdg",name:"kslfjskd",test:{test:12,another:"value"}},token_secret:"jfkjdsj",hash_rounds:"kjsfkljdfkl"};
const convert = o =>
Object.fromEntries(
Object.entries(o).map(([k, v]) =>
[k.toUpperCase(), Object(v) === v ? convert(v) : v]
)
)
console.log(convert(development))

javascript implementation of lodash "set" method

Found this excellent code for _.get vanilla js implementation:
const get = (obj, path, defaultValue) => path.split(".")
.reduce((a, c) => (a && a[c] ? a[c] : (defaultValue || null)), obj)
Now I'm looking for _.set implementation, any help would be appreciated.
I think this could cover it:
const set = (obj, path, value) => {
if (Object(obj) !== obj) return obj; // When obj is not an object
// If not yet an array, get the keys from the string-path
if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [];
path.slice(0,-1).reduce((a, c, i) => // Iterate all of them except the last one
Object(a[c]) === a[c] // Does the key exist and is its value an object?
// Yes: then follow that path
? a[c]
// No: create the key. Is the next key a potential array-index?
: a[c] = Math.abs(path[i+1])>>0 === +path[i+1]
? [] // Yes: assign a new array object
: {}, // No: assign a new plain object
obj)[path[path.length-1]] = value; // Finally assign the value to the last key
return obj; // Return the top-level object to allow chaining
};
// Demo
var obj = { test: true };
set(obj, "test.1.it", "hello");
console.log(obj); // includes an intentional undefined value
It is a bit more complex than get, because there is some logic needed to create missing parts of the path in the object, to overwrite primitive values that stand in the way, and to determine whether a new child should better be an array or a plain object.
Check this one:
/**
* #example
* const obj = {id:1, address: {city: 'Minsk', street: 'Prityckogo 12'}}
* setByString(obj, 'address.city', 'Grodno'); obj.address.city => 'Grodno'
* setByString(obj, ['address', 'city'], 'Grodno'); obj.address.city => 'Grodno'
* setByString(obj, ['address', 'city', 'phones'], {mobile: 1234, home: 222}); obj.address.city.phones.home => 222
*/
/**
* #param {any} input
* #return {boolean}
*/
const isObject = (input) => (
null !== input &&
typeof input === 'object' &&
Object.getPrototypeOf(input).isPrototypeOf(Object)
)
**/
* #param {object} obj
* #param {string} path
* #param {any} value
*/
const setByString = (obj, path, value) => {
const pList = Array.isArray(path) ? path : path.split('.');
const len = pList.length;
// changes second last key to {}
for (let i = 0; i < len - 1; i++) {
const elem = pList[i];
if (!obj[elem] || !isObject(obj[elem])) {
obj[elem] = {};
}
obj = obj[elem];
}
// set value to second last key
obj[pList[len - 1]] = value;
};
const set = (obj = {}, paths = [], value) => {
const inputObj = obj === null ? {} : { ...obj };
if (paths.length === 0) {
return inputObj;
}
if (paths.length === 1) {
const path = paths[0];
inputObj[path] = value;
return { ...inputObj, [path]: value };
}
const [path, ...rest] = paths;
const currentNode = inputObj[path];
const childNode = set(currentNode, rest, value);
return { ...inputObj, [path]: childNode };
};
Example:
const input = {};
set(input, ['a', 'b'], 'hello');
Result:
{ a: { b: 'hello' }}
Here's an implementation of lodash 'get' and 'set' without using dot or bracket notation; useful for passing security scans.
https://jsfiddle.net/5amtL8zx/17/
/* lodash implementation of 'get', 'set', and 'unset' without dot or bracket notation
* - supports getting and setting 'prop1.2' array element but not with brackets: 'prop1.[2]'
*/
isObjectKey = (obj, key) => {
return Object.getPrototypeOf(obj) === Object.prototype && /string|number/.test(typeof key);
}
isArrayNumber = (obj, key) => {
const isKey = /string|number/.test(typeof key), path = isKey ? String(key).split('.') : [], prop = isKey && path.length > 1 ? path.shift() : '';
return Object.getPrototypeOf(obj) === Array.prototype && isKey && !isNaN(prop);
}
isValid = (obj, key) => {
const isObj = isObjectKey(obj, key), isArr = isArrayNumber(obj, key);
return isObj || isArr;
}
define = (obj, key, value) => {
Object.defineProperty(obj, String(key), { value, writable: true, configurable: true, enumerable: true });
}
get = (obj, key, value) => {
if (!isValid(obj, key)) {
return undefined;
}
let path = String(key).split('.'), prop = path.shift(), result = new Map(Object.entries(obj)).get(prop);
return path.length && typeof result !== 'undefined' ? get(result, path.join('.'), value) : result || value;
}
set = (obj, key, value) => {
if (!isValid(obj, key)) {
return undefined;
}
let path = key.split('.'), prop = path.shift();
if (!(prop in obj)) {
define(obj, prop, {});
}
const result = get(obj, prop);
return path.length && isValid(result, path.join('.')) ? set(result, path.join('.'), value) : define(obj, prop, value);
}
unset = (obj, key) => {
if (!isValid(obj, key)) {
return undefined;
}
let path = key.split('.'), prop = path.shift();
if (!(prop in obj)) {
return undefined;
}
if (path.length) {
let result = get(obj, prop);
result = unset(result, path.join('.'));
set(obj, prop, result);
return obj;
} else {
const { [prop]: remove, ...rest } = obj;
return rest;
}
}
let obj = {};
set(obj, 'prop1.prop2', 'value1');
console.log(Object.entries(obj));
console.log(get(obj, 'prop1.prop2'));
const prop1 = get(obj, 'prop1');
set(prop1, 'prop2', 'value2');
console.log(get(obj, 'prop1.prop2'));
set(obj, 'prop3', [1, 2, 3]);
console.log(get(obj, 'prop3'));
console.log(get(obj, 'prop3.2'));
console.log(get(obj.prop3, 0));
set(obj, 'prop3.3', 4);
console.log(get(obj, 'prop3.3'));
set(obj, 'prop4', [{'name': 'Bob'}]);
console.log(get(obj, 'prop4.0'));
unset(obj, 'prop4.0.name')
console.log(get(obj, 'prop4.0'));
//[["prop1", {
// prop2: "value1"
//}]]
//"value1"
//"value2"
//[1, 2, 3]
//3
//1
//4
//{
// name: "Bob"
//}
//{ ... }

Create object with only defined properties

I have a function which returns an object with properties only which are defined.
How to refactor the function so that I don't need to make if clauses for every parameter value? There must be more elegant way to do this.
const getQuery = ({ foo, bar, zoo }) => {
const query = {};
if (foo) {
query.foo = foo;
}
if (bar) {
query.bar = bar;
}
if (zoo) {
query.zoo = zoo;
}
return query;
}
I would do something like
function getQuery(obj){
// filter the accepted keys
var filtered = Object.keys(obj).filter((k) => ~["foo", "bar", "baz"].indexOf(k))
// construct new object with filtered keys
var query = {}
filtered.forEach((k) => query[k] = obj[k])
return query
}
Here's a basic function that will copy only properties provided in the wantedProps array. It will not mutate the original object.
let filterProperties = (originalObject = {}, wantedProps = []) =>
{
let filteredObject = {};
wantedProps.forEach( val => filteredObject[val] = originalObject[val] );
return filteredObject;
}
If you're just trying to filter out undefined vals then you could do:
obj => {
let newObject = {}
Object.keys(obj).forEach(key => {
if(obj[key] !== undefined) newObject[key] = obj[key];
})
return newObject;
}

Categories

Resources