Deleting key to nested object? - javascript

In this question I got some excellent answers, but it turned out I had over simplified the problem I am facing.
I have the same object
const obj = {
a: 'A',
b: {
bb: 'BB',
bbb: 'BBB',
},
c: 'C'
};
and I somehow need to end up with
{ a: 'A', bb: 'BB', bbb: 'BBB', c: 'C' }
where there key to the nested object is removed. It doesn't have to be in place. Creating a new object is fine.
Question
Can anyone figure out how to delete the key from the nested object, but still keep the nested object?

You can flatten it recursively:
function flattenObject(obj) {
const ret = {};
for (const [key, val] of Object.entries(obj)) {
if (typeof val === "object") {
Object.assign(ret, flattenObject(val));
} else {
ret[key] = val;
}
}
return ret;
}

Get an array of [key, value] pairs using Object.entries(). Iterate the pairs with Array.flatMap(). If the value is object, call the function with the value. If not return an object of { [key]: value }. Merge to a single object by spreading the array of object into Object.assign().
Use recursion and merge the sub objects by spreading into Object.assign():
const fn = obj => Object.assign({},
...Object.entries(obj)
.flatMap(([k, v]) => typeof(v) === "object" ? fn(v) : { [k]: v })
);
const j = { a: 'A', b: { bb: 'BB', bbb: 'BBB' }, c: 'C' };
const result = fn(j);
console.log(result);
Another option is to work directly with the [key, value] entries (p), and then convert everything to a single object using Object.fromEntries():
const fn = obj =>
Object.entries(obj)
.flatMap(p => typeof(p[1]) === "object" ? fn(p[1]) : [p])
const j = { a: 'A', b: { bb: 'BB', bbb: 'BBB' }, c: 'C' };
const result = Object.fromEntries(fn(j));
console.log(result);

Related

Best way to create an array from an object that has number of copies

From an object like this:
{a:1, b: 2, c: 3}
I would like to turn into
['a', 'b', 'b', 'c', 'c', 'c']
Where the key is the string and the value is the number of copies, order doesn't matter.
What's the best way to do this?
I was thinking about using array.fill but not sure if that's actually easier than just iterating and push.
Edit: Currently this:
const arr = []
_.each(obj, function (v, k) {
_.times(v, function () {
arr.push(k)
})
})
You could flatMap the Object.entries and fill an array of each size.
const obj = { a: 1, b: 2, c: 3 };
const result = Object.entries(obj).flatMap(([k, v]) => Array(v).fill(k));
console.log(result)
or with Lodash
const obj = { a: 1, b: 2, c: 3 };
const arr = _.flatMap(obj, (v,k) => Array(v).fill(k))
console.log(arr);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
But there's nothing like a simple loop
const obj = { a: 1, b: 2, c: 3 };
const result = []
for (let [k, v] of Object.entries(obj)) {
while (v--) {
result.push(k)
}
}
console.log(result)
I would convert the object into an array of keys using Object.keys and then use a newly created empty results array, then map through the keys.
For each key I would add a fill array to the existing results.
Here's the ES6 solution to that (no extra libraries required)
const obj = { a: 1, b: 2, c: 3 };
let result = []
Object.keys(obj).forEach(key => {
result = [...result, ...new Array(obj[key]).fill(key)]
})
console.log(result)
You can use Object.entries and Array#reduce as follows:
const input = {a:1, b: 2, c: 3};
const output = Object.entries(input).reduce(
(prev, [key,value]) => prev.concat( Array(value).fill(key) ),
[]
);
console.log( output );
Or, using Array#push instead of Array#concat,
const input = {a:1, b: 2, c: 3};
const output = Object.entries(input).reduce(
(prev, [key,value]) => prev.push( ...Array(value).fill(key) ) && prev,
[]
);
console.log( output );
Or, using for loops,
const input = {a:1, b: 2, c: 3};
const output = [],
pairs = Object.entries(input);
for(let i = 0; i < pairs.length; i++) {
const [key, value] = pairs[i];
for(let j = 0; j < value; j++) {
output.push( key );
}
}
console.log( output );

Compare two objects and replace values with same key - keep/ignore the rest

I am try merge two objects, but no really merge. I would like to only replace the values of the keys in common - ignore the rest.
obj1 = { a: 'replace a', b: 'keep b' }
obj2 = { a: 'new value a', c: 'ignore c' }
expected result:
console.log(obj1)
// { a: 'new value a', b: 'keep b' }
I tried a couple different things like:
for (const [k, v] of Object.entries(obj2)) {
obj1[k] = v
}
but not really what I was going for. And keep getting this Type error:
Element implicitly has an 'any' type because expression of type
'string' can't be used to index type ... No index signature with a
parameter of type 'string' was found on type ...
You could iterate the keys of obj2 and check if the key exists in obj1, then update with value.
const
obj1 = { a: 'replace a', b: 'keep b' },
obj2 = { a: 'new value a', c: 'ignore c' };
for (const key in obj2) if (key in obj1) obj1[key] = obj2[key];
console.log(obj1);
You can use this approach.
obj1 = { a: 'replace a', b: 'keep b' }
obj2 = { a: 'new value a', c: 'ignore c' }
newObj = {}
for (const key in obj1) {
if (Object.hasOwnProperty.call(obj1, key)) {
const value = obj1[key];
newObj[ key ] = value;
if( Object.hasOwnProperty.call(obj2, key) ){
const value2 = obj2[key];
newObj[ key ] = value2;
}
}
}
console.log( newObj )
Loop over the object with the new values and update the values of the original object if the key is already present.
const obj1 = { a: 'replace a', b: 'keep b' };
const obj2 = { a: 'new value a', c: 'ignore c' };
const mergeObjects = (obj1, obj2) => {
// Create new object to prevent overwriting the original.
const merged = Object.assign({}, obj1);
for (const key of Object.keys(obj2)) {
if (key in merged) {
merged[key] = obj2[key];
}
};
return merged;
}
const result = mergeObjects(obj1, obj2);
console.log(result);

Omit keys from an object which are present in another array in javascript

I have an object like this -
const obj = {
'a': 1,
'b': 2,
'c': 3,
'd': 4,
'e': 5
}
and an array containing keys belonging to the object,
const arr = ['a', 'b', 'd']
I want to return an object similar to obj but it shouldn't contain the keys present in arr,
i.e., I want an object which will be
const result = {
'c': 3,
'e': 5
}
How can I achieve this via Javascript?
With the new Object.fromEntries, it's easy:
const result = Object.fromEntries(Object.entries(obj).filter(([k, _]) => !arr.includes(k)))
Without it, it takes a bit more effort:
const result = Object.entries(obj).reduce((tot, [k, v]) => {
if (!arr.includes(k)) {
tot[k] = v;
}
return tot;
}, {})
You can copy the object and delete the listed properties:
const obj = {a:1,b:2,c:3,d:4,e:5};
const arr = ['a', 'b', 'd'];
const res = {...obj};
for (let k of arr) delete res[k];
console.log(res);
Note that this has a time complexity that is almost linear in terms of the number of properties involved, O(m+nlogn) (if delete is O(logn)), while Aplet's answer would have a quadratic time complexity: O(mn).
Using Object.entries, you can generate [key, value] pair from object as array.
And based on that, using Array.reduce, you can get the result you want.
const obj = {
'a': 1,
'b': 2,
'c': 3,
'd': 4,
'e': 5
};
const arr = ['a', 'b', 'd'];
const result = Object.entries(obj).reduce((acc, cur) => {
if (!arr.includes(cur[0])) {
acc[cur[0]] = cur[1];
}
return acc;
}, {});
console.log(result);
Array#reduce solution:
const obj = {'a': 1,'b': 2,'c': 3,'d': 4,'e': 5};
const arr = ['a', 'b', 'd'];
const o = Object.keys(obj)
.reduce((s, a) => (arr.indexOf(a) > -1 ? s : s[a] = obj[a], s), {});
console.log(o);
You could destructure the object and get the rest of it without unwanted keys.
const
object = { a: 1, b: 2, c: 3, d: 4, e: 5 },
without = ['a', 'b', 'd'],
result = without.reduce((r, k) => ({ [k]: _, ...r } = r, r), object);
console.log(result);
Traverse the object using JavaScript for..in loop. Check property using array indexOf method. If property name is not present in the array then put the property with value in ret variable.
const obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
};
const arr = ["a", "b", "d"];
ret = {};
for (let x in obj) if (arr.indexOf(x) === -1) ret[x] = obj[x];
console.log(ret);
You can do this using Object#entries then filter out those entries which are not in the arr and using Object#fromEntries reconstruct the object:
const obj = {'a': 1,'b': 2,'c': 3,'d': 4,'e': 5}, arr = ['a', 'b', 'd'];
const filterObj = (obj, arr) => {
const dataSet = new Set(arr);
return Object.fromEntries(Object.entries(obj)
.filter(([k, v]) => !dataSet.has(k)))
};
console.log(filterObj(obj, arr));
Another way to filter the object with the supplied keys but without using Object#fromEntries is by using Object#assign.
We would just filter the entries which are not in the given array, then map them to objects and pass those as parameters to the Object#assign:
const obj = {'a': 1,'b': 2,'c': 3,'d': 4,'e': 5}, arr = ['a', 'b', 'd'];
const filterObj = (obj, arr) => {
const keySet = new Set(arr);
const filteredEntries = Object.entries(obj).filter(([k, v]) => !keySet.has(k));
return Object.assign({}, ...filteredEntries.map(([k, v]) => ({[k]: v})));
}
console.log(filterObj(obj, arr));

How to set the same value on multiple keys

How is it possible to set the same value on multiple keys? For example I have the following object:
const obj = {
a: 5,
b: 5,
c: 5,
d: 6
}
Is there any easy way for example lets say I have array of [a, b, c] (keys) and to spread them in object and set them as keys with same value. Point is to look more classy for example:
const keys =[a, b, c]
const obj = {
[...keys]: 5
}
I know this would throw error but looking for some shorthand to achieve this
Take the array of keys and map each to an entry of the key and the 5 value:
const keys = ['a', 'b', 'c'];
const obj = {
...Object.fromEntries(
keys.map(key => [key, 5])
),
d: 6
};
console.log(obj);
Something like this works:
const obj = {};
const value = 5;
['a', 'b', 'c'].forEach(key => obj[key] = value);
You can build a zip function which takes an array of keys and a array of values and returns an object where each key/value pair is taken from their respective index:
const zip =
(ks, vs) =>
ks.reduce((o, k, i) =>
(o[k] = vs[i], o), {});
zip(['a', 'b', 'c'], [5, 5, 5]);
//=> {a: 5, b: 5, c: 5}
If you need to generate an array of 5:
Array(3).fill(5);
//=> [5, 5, 5]
Another solution could take a spreading of Object.assign with a spreaded array of objects.
const
keys = ['a', 'b', 'c'],
obj = { ...Object.assign(...keys.map(k => ({ [k]: 5 }))), d: 6 };
console.log(obj);
function(item)
{
return Object.keys(item).reduce((a, b) => ({ ...a, [b]:5 }),{});
}

Remove from JS object where key value is an empty array

I'm trying to remove keys from an object where the values is Array(0). Here's the object:
{fruit: Array(1), dairy: Array(2), vegetables: Array(0)}
This is the desired result:
{fruit: Array(1), dairy: Array(2)}
So far, I've been playing with the delete operator and .filter/.reduce methods.
Any help would be awesome :)
Just iterate over the keys of the object, check if the value for that key is an empty array and if so, delete it:
let obj = {
a: [1],
b: [],
c: 5,
d: false
}
for (const key in obj) { if (Array.isArray(obj[key]) && !obj[key].length) delete obj[key] };
console.log(obj);
The filter/reduce operators are for Arrays not for objects. If you must use the filter/reduce operators, you can try:
const obj = {a: [1], b: [1,2], c: []};
const filtered = Object.keys(obj)
.filter(key => Array.isArray(obj[key]) && obj[key].length != 0)
.reduce((acc, key) => {acc[key] = obj[key]; return acc}, {});
console.log(filtered);

Categories

Resources