This question already has answers here:
Filter object properties by key in ES6
(30 answers)
Closed 1 year ago.
I'm trying to create an object from object and list of properties.
const pick = (obj, ...fields) => {
return [...fields] = obj
};
How can I realise this?
Reduce the list of fields, and take the values from the original object:
const pick = (obj, ...fields) => fields.reduce((acc, field) => ({ ...acc, [field]: obj[field] }), {});
const obj = { a: 1, b: 2, c: 3 };
const result = pick(obj, 'a', 'c');
console.log(result);
You can use the in operator to ignore properties that don't exist on the original object:
const pick = (obj, ...fields) => fields.reduce((acc, field) => {
const value = obj[field];
if(field in obj) acc[field] = value;
return acc;
}, {});
const obj = { a: 1, b: 2, c: 3 };
const result = pick(obj, 'a', 'c', 'd');
console.log(result);
Try something like this:
const pick = (obj, ...fields) => Object.fromEntries(Object.entries(obj).filter(([k]) => fields.includes(k)))
Iterate through fields array and check if property is available in obj then put into final object which needs to be returned.
const pick = (obj, ...fields) => {
const finalObj = { };
for (field of fields) {
if (obj[field]) {
finalObj[field] = obj[field];
}
}
return finalObj;
};
const obj = { name: "test name", age: 25, title: "Mr.", city: "test city" };
console.log(pick(obj, "name", "age", "city", "other"));
Related
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));
Hi I'm trying to show a loader for each input field, but only when they have entered a value. The behaviour i'm getting is if i enter a value into one of the text inputs the spinner shows for all. I need to determine my loading state based on the difference of the two arrays, by the array key.
Similar to this post -> How to get the difference between two arrays of objects in JavaScript
const [isLoading, setIsLoading] = useState('');
// before any value has been entered
const initialFormData = [{key: 'USER_DEFINED_QUESTIONS', value: ''}, {key: 'RECOMMENDED_FIELDS', value: ''}]
// when a value has been entered
const values = [{key: 'USER_DEFINED_QUESTIONS', value: 'test'}, {key: 'RECOMMENDED_FIELDS', value: ''}]
const saveSubmit = async (values) => {
const arrValues = Object.entries(values).map((val) => ({
field: val[0],
value: val[1],
}));
const arrInitialFormValues = Object.entries(initialFormData).map(
(val) => ({
field: val[0],
value: val[1],
}),
);
const result = arrValues.filter(
(o1) => !arrInitialFormValues.some((o2) => o1.key === o2.key),
);
const body = { ...values };
setIsLoading(result);
const res = await putResults({ id, body });
if (res && !(await checkErrors(res))) {
return res;
}
setIsLoading(result);
}
return null;
};
You can combine the renaming and filter
const saveSubmit = async (values) => {
const result = initialFormData.reduce((acc, a) => {
const diff = values.find((b) => a.key === b.key && a.value !== b.value)
return diff
? [
...acc,
{
field: diff.key,
value: diff.value,
},
]
: acc
}, [])
const body = { ...values }
setIsLoading(result)
const res = await putResults({ id, body })
if (res && !(await checkErrors(res))) {
return res
}
setIsLoading(result)
}
First of all, I would map the array into an object (this is not needed, it just feels cleaner this way).
values = Object.fromEntries(a.map(entry => [entry.key, entry.value]))
Then I believe, all you really need is to find all entries that have non-negative "value" (if that is not the case, please comment below and I will correct this answer).
usedFields = Object.entries(values).filter(entry => entry[1])
If you want only to get the difference, and that is what i can read from question title, shouldn't this work?
const initialFormData = [{key: 'USER_DEFINED_QUESTIONS', value: ''}, {key: 'RECOMMENDED_FIELDS', value: ''}];
// when a value has been entered
const values = [{key: 'USER_DEFINED_QUESTIONS', value: 'test'}, {key: 'RECOMMENDED_FIELDS', value: ''}];
const keysWhichDiffer = values.map(x => initialFormData.find(y=> x.key===y.key && x.value!==y.value)?.key);
const result = keysWhichDiffer.filter(x=>x);
console.log('res', result)
I'm using the following piece of code (which is working fine)
const result = {}
Object.keys(timers).forEach(key => {
result[key] = hydrate(timers[key])
})
return result
}
I'm wondering if this is possible in one method? So without having to fill the result object?
Convert to entries with Object.entries(), iterate the entries with Array.map() and hydrate the values, and convert back to an object with Object.fromEntries():
const fn = timers => Object.fromEntries(
Object.entries(timers).map(([k, v]) => [k, hydrate(v)])
)
Just use reduce
var timers = {
a: 2,
b: 3,
c: 4
}
const hydrate = x => 2*x
var result = Object.entries(timers).reduce((o, [key, value]) => {
o[key] = hydrate(value)
return o
}, {})
console.log(result)
without fat arrow
var timers = {
a: 2,
b: 3,
c: 4
}
function hydrate (x) { return 2 * x }
var result = Object.entries(timers).reduce(function(o, entry) {
o[entry[0]] = hydrate(entry[1])
return o
}, {})
console.log(result)
I'm trying to get this:
const obj = {
keyFirst: 'firstVal',
keySecond: 'secondVal',
};
from this:
const obj = {
key_first: 'firstVal',
key_second: 'secondVal',
};
...so to change underscore key format to camelCase format.
I tried to achieve it by using reduce function:
const renameKeys = obj => Object
.keys(obj)
.reduce((acc, key) => {
const modifiedKey = key.replace(/_([a-z])/g, function f(g) {
return g[1].toUpperCase();
});
return ({
...acc,
...{ [modifiedKey]: obj[key] },
}, {});
});
console.log(renameKeys(obj));
But this won't work and return empty objects. How can I change it to achieve my goal?
The problem is
return ({
...acc,
...{ [modifiedKey]: obj[key] },
}, {});
You're unintentionally using the comma operator - although the new object is evaluated, the whole expression inside the parentheses evaluates to the final value in the comma-separated list, which is the empty object, which becomes the accumulator on the next iteration.
Don't use the comma operator in the return, and pass {} as a second argument to reduce instead:
const obj = {
key_first: 'firstVal',
key_second: 'secondVal',
};
const renameKeys = obj => Object
.keys(obj)
.reduce((acc, key) => {
const modifiedKey = key.replace(/_([a-z])/g, function f(g) {
return g[1].toUpperCase();
});
return ({
...acc,
...{ [modifiedKey]: obj[key] },
});
}, {});
console.log(renameKeys(obj));
Also, rather than using Object.keys, you might consider Object.entries instead, because [modifiedKey]: value is probably a bit clearer than [modifiedKey]: obj[key]:
const obj = {
key_first: 'firstVal',
key_second: 'secondVal',
};
const renameKeys = obj => Object
.entries(obj)
.reduce((acc, [key, val]) => {
const modifiedKey = key.replace(/_([a-z])/g, g => g[1].toUpperCase());
return ({
...acc,
...{ [modifiedKey]: val },
});
}, {});
console.log(renameKeys(obj));
You can split the key name by group and use $1 & $2 where these represent group
const obj = {
keyFirst: 'firstVal',
keySecond: 'secondVal',
};
let newObj = {}
for (let keys in obj) {
newObj[keys.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase()] = obj[keys]
}
console.log(newObj)
I am trying to redeclare variables defined in global scope. I'd like to wrap each function
const {values, map, each} = require('lodash')
const wrapFnInLog = (fn) => (...input) => {
console.log({name: fn.name, input})
const possiblePromise = fn.apply(null, input)
if (get(possiblePromise, 'then')) {
return possiblePromise.then(output => {
console.log({name: fn.name, output})
return output
})
} else {
console.log({name: fn.name, output: possiblePromise})
return possiblePromise
}
}
let a = (arr) => map(arr, i => i.name)
let b = (obj) => a(values(obj))
const provide = [a, b]
provide.forEach(fn => wrapFnInLog(fn))
const example = {
personTom: {
name: 'Tom'
},
personJerry: {
name: 'Jerry'
}
}
b(example)
I'd like the output to look like this:
{ name: 'b', input: [ { personTom: [Object], personJerry: [Object] } ] }
{ name: 'a', input: [ [ [Object], [Object] ] ] }
{ name: 'a', output: [ 'Tom', 'Jerry' ] }
{ name: 'b', output: [ 'Tom', 'Jerry' ] }
The only way I've been able to achieve this is without a loop and it's via deliberately overwriting each variable one by one.
a = wrapFnInLog(a)
b = wrapFnInLog(b)
I'm wondering if it's possible to loop over [a, b] and overwrite the function definition, while keeping them in global module scope.
as already commented, you can use a destructuring assignment to assign multiple variables at once
let a = (arr) => map(arr, i => i.name);
let b = (obj) => a(values(obj));
[a,b] = [a,b].map(wrapFnInLog);
but unlike a destructuring assignment in combination with a variable declaration (let [a,b] = ...) you have to be careful what you write before this assignment and that you properly seperate commands.
Because with automatic semicolon insertation or, JS not inserting a semicolon where one should be,
let a = (arr) => map(arr, i => i.name)
let b = (obj) => a(values(obj))
[a,b] = [a,b].map(wrapFnInLog)
will be interpreted as
let a = (arr) => map(arr, i => i.name);
let b = (obj) => {
return a(values(obj))[a,b] = [a,b].map(wrapFnInLog);
}
//or in other words
let b = (obj) => {
let tmp1 = a(values(obj));
a; //the `a,` in something[a,b];
let tmp2 = [a,b].map(wrapFnInLog);
tmp1[b] = tmp2;
return tmp2;
}