ES6 spread syntax : skip empty keys automatically? - javascript

What is simpler pattern to avoid updating inputs if one of the keys is empty in payload ?
Is there a nice ES6 syntax ?
const master = {
inputs: {a: [], b: [], c: []}
};
const {a, b, c} = payload;
const updateMaster = (payload) => ({
...master, inputs: {...master.inputs, ...payload}
});

To filter the fields of an object, use Object.entries to retrieve the fields, Array.prototype.filter to filter then, and Object.formEntries to reconstruct an object from the filtered entries.
let payload = {
a: [],
b: [1, 2]
};
let nonEmptyPayload = Object.fromEntries(Object.entries(payload).filter(([_, v]) => v.length))
console.log(nonEmptyPayload);
Applying this to your example,
let master = {
inputs: {
a: [],
b: [13, 14],
c: [10, 12]
}
};
let trimObj = obj => Object.fromEntries(Object.entries(obj).filter(([_, v]) => v.length));
let updateMaster = payload => ({
...master,
inputs: { ...master.inputs,
...trimObj(payload)
}
});
updateMaster({
b: [15, 16], // Will override master.c
c: [] // Will not override master.c
});
console.log(master);

You could create a function like this. It removes all empty values from an object, without directly modifying the object passed to the function.
const removeEmpty = obj => {
return Object.keys(obj).reduce((acc, key) => {
// value is "falsey" or is empty array
return !obj[key] || (Array.isArray(obj[key]) && !obj[key].length)
? acc
: {...acc, [key]: obj[key]}
}, {})
}
console.log(removeEmpty({a: 'AAA', b: '', c: 'CCC', d: false, e: null, f: [1,2], g: []}))
So your final snippet would look like this:
const updateMaster = (payload) => ({
...master, inputs: {...master.inputs, ...removeEmpty(payload)}
});

Related

Reordering an object to the keys are in the same order as an array

I have an array that looks like this,
['event_tag', 'workflow_tag', 'created_timestamp', 'success']
and an array of objects where the object looks like,
{
"created_timestamp": "2022-04-01T13:14:53.028002Z",
"workflow_tag": "dj807",
"event_tag": "refresh",
"success": true
}
What I am wanting to do is make the above object and any other objects in that array match the order of the values in the first array so the finished object should look like,
{
"event_tag": "refresh",
"workflow_tag": "dj807",
"created_timestamp": "2022-04-01T13:14:53.028002Z",
"success": true
}
I have tried the following so far,
const keys = ['event_tag', 'workflow_tag', 'created_timestamp', 'success'];
newRowData = parsedRows.reduce((obj, v) => {
obj[v] = keys[v];
return obj
}, {});
But this returns,
{[object Object]: undefined}
You could order the keys by constructing a new object inside of an Array#map:
const parsedRows = [ { a: 1, c: 3, d: 4, b: 2, }, { b: 6, a: 5, c: 7, d: 8, }, { d: 12, b: 10, a: 9, c: 11, }, ];
const order = ['a', 'b', 'c', 'd'];
let newData = parsedRows.map(row => {
let newRow = {};
for (let key of order) {
newRow[key] = row[key];
}
return newRow;
});
console.log(newData);
Instead of iterating over Rows, Iterate on keys either map/reduce.
const keys = ["event_tag", "workflow_tag", "created_timestamp", "success"];
const obj = {
created_timestamp: "2022-04-01T13:14:53.028002Z",
workflow_tag: "dj807",
event_tag: "refresh",
success: true,
};
const res = Object.assign({}, ...keys.map((key) => ({ [key]: obj[key] })));
console.log(res)

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 );

How to swap key-value and only return the first pair in javascript?

Here is the task
#param {object} ???
#returns {object} an object with the given object's values as keys, and keys as values. If there are duplicate values in the input object, only the first key-value pair should be used and subsequent pairs with the same value discarded.
2.Here is what i did code
What's wrong with it? How should i rework?
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
function swapPairs2(obj){
let newObject = {};
for(const key in obj){
let value = obj[key]
newObject[value] = key;
}
return newObject;
}
3.Here are the tests
test(swapPairs2(object5), { 1: "a", 2: "b", 3: "c" });
test(swapPairs2(object6), { 1: "a" });
4.Here is the error i got
I am supposed to get the first key-value pair, but a random return.
You can also reverse the entries.
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
const swapPairs2 = obj => Object.fromEntries(
Object.entries(obj)
.reverse() // Keep the first, not the last
.map(entry => [entry[1], entry[0]]) // Reverse the key and vlaue
);
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));
Only add the value/key pairs, if the value doesn't already exist on newObject by checking with the in operator:
function swapPairs2(obj) {
const newObject = {};
for (const key in obj) {
const value = obj[key];
if(!(value in newObject)) {
newObject[value] = key;
}
}
return newObject;
}
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));
Another option is to use Array.reduce():
const swapPairs2 = obj =>
Object.entries(obj)
.reduce((acc, [k, v]) =>
v in acc
? acc
: { ...acc, [v]: k }
, {})
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));
You can do it by taking the object keys and using the Array.reduce() method on the keys.
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
const swapPairs2 = (obj) => Object.keys(obj)
.reduce((a, c) => (a[obj[c]] = a[obj[c]] ?? c, a), {});
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));
Or using the Array.reduce() method on the Object.entries()
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
const swapPairs2 = obj => Object.entries(obj)
.reduce((acc, [key, val]) => (acc[val] = acc[val] ?? key, acc), {})
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));

JavaScript, split object in 2, and push in array

I have an array of objects like this:
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 }
]
And another array which is called "headers"
const headers = [
['A', 'B'],
['C']
]
I have to create an array similar to the first one but, with those objects splited by what headers have in it's arrays.
This should be the goal:
const result = [
[
{ A: 1, B: 2 },
{ C: 3 }
],
[
{ A: 3, B: 4 },
{ C: 1 }
]
]
I tried by doing a "base" array with:
const baseArray = []
headers.forEach((header) => {
const objFromHeader = {};
header.forEach((head) => {
objFromHeader[head] = 0;
});
baseArray.push(objFromHeader);
});
That will give me the result array but with 0 values for each key.
And then loop for the first array and put inside another array the base array with the correct values.
Then I wanted to fill each key according to the value that comes from arrayOfObjects but here is where I can't see how could I loop that array of objects and put the correct value. The only problem with that approach is that the result array will have some 0 values that come from the initiation array that I'm using, it would be better to me to only put the objects that actually have values and not 0 (I was thinking on another function to delete those keys with value = 0...)
How could I achieve it in a better way?
Fiddle:
https://jsfiddle.net/pmiranda/Lpscz6vt/
When iterating over an object, use findIndex on the headers to identify which index in the headers array the property being iterated over should go into. Create an object there if it doesn't exist yet, and set the property.
const arrayOfObjects = [
{ A: 1, B: 2, C:3 },
{ A: 3, B: 4, C:1 }
];
const headers = [
['A', 'B'],
['C']
];
const toResultItem = (object) => {
const resultItem = [];
for (const [key, value] of Object.entries(object)) {
const headersIndex = headers.findIndex(arr => arr.includes(key));
resultItem[headersIndex] ??= {};
resultItem[headersIndex][key] = value;
}
return resultItem;
};
console.log(arrayOfObjects.map(toResultItem));
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 },
];
const headers = [['A', 'B'], ['C', 'D']];
const result = arrayOfObjects.map((obj) =>
headers.map((header) =>
header.reduce((acc, key) => {
acc[key] = obj[key];
return Object.keys(acc).reduce((newAcc, key) => {
if (acc[key]) {
newAcc[key] = acc[key];
}
return newAcc;
}
, {});
}, {})
)
);
console.log(result);
Array.forEach implementation
Logic
Loop through arrayOfObjects array.
Inside that, loop through headers array.
Inside that, loop through each array in the headers array.
Create an empty object and assign the property from nodes in headers array with values from objects in arrayOfObjects array.
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 }
];
const headers = [
['A', 'B'],
['C']
];
const baseArray = []
arrayOfObjects.forEach((obj) => {
const childNode = [];
headers.forEach((head) => {
const node = {};
head.forEach((key) => node[key] = obj[key]);
childNode.push(node);
});
baseArray.push(childNode);
});
console.log(baseArray)
Array.map and Array.reduce implementation.
Using the same logic implementes in the above solution, we can rewrite this using Array.map and Array.reduce as below.
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 }
];
const headers = [
['A', 'B'],
['C']
];
const output = arrayOfObjects.map((obj) => {
return headers.map((header) => {
return header.reduce((acc, curr) => {
acc[curr] = obj[curr];
return acc;
}, {});
})
})
console.log(output);

update nested json object using recursion in JavaScript

I'm trying to create an updated object from an existing object.
The sample object is:
// sample object
const testObj = {
a: 1,
b: {
c: 2,
d: {
e: 3,
f: {
g: 4
}
}
}
};
I want to create a new object from the above object with some concatenation of each value:
// expected object
const expectedObject= {
a: '1 a',
b: {
c: '2 a',
d: {
e: '3 a',
f: {
g: '4 a'
}
}
}
};
here is my sample code:
let expectedObject = {};
const newObject = object => {
Object.entries(object).forEach(([key, value]) => {
if (typeof value === "object") {
Object.keys(value).map(key => {
value[key] = value[key] + " a";
return value;
});
expectedObject[key] = value;
//return newTest;
} else {
expectedObject[key] = value;
return expectedObject;
}
});
return expectedObject;
};
console.log(newObject(testObj));
the outcome in console is:
{a: 1, b: {…}}
a: 1
b:
c: "2 a"
d: "[object Object] a"
__proto__: Object
__proto__: Object
I wanted to use recursion here and also tried it but no luck.
any help, thanks?
You could get a new object my mapping changed values and creating new objects.
function map(object, fn) {
return Object.fromEntries(Object
.entries(object)
.map(([k, v]) => [k, v && typeof v === 'object' ? map(v, fn) : fn(v)])
);
}
var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4 } } } },
result = map(object, v => v + ' a');
console.log(result);
If you have arrays inside, you could add a check in advance and map the values.
const
map = fn => {
const iter = v => v && typeof v === 'object'
? Array.isArray(v)
? v.map(iter)
: Object.fromEntries(Object.entries(v).map(([k, v]) => [k, iter(v, fn)]))
: fn(v);
return iter;
};
var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4, k: [5, 6] } } } },
addA = map(v => v + ' a'),
result = addA(object);
console.log(result);
This is simply a refactoring of the answer from #user633183. I like that approach a lot, but think it can be simplified by extracting two more reusable functions. This started as a comment on that answer, but I thought it would be better to be explicit.
const map = (f) => (a) =>
a.map(f)
const mapObj = (f) => (o) =>
Object .entries (o) .reduce ( (a, [k, v] ) => ({ ...a, [k]: f(v) }), {})
const traverse = (f) => (t) =>
Array.isArray(t)
? map (traverse (f)) (t)
: Object(t) === t
? mapObj (traverse (f)) (t)
: f (t)
const input =
{ a: [ 1, 11, 111 ], b: { c: 2, d: { e: [ 3, { f: { g: 4 } } ] } } }
const output =
traverse(x => `${x} a`) (input)
console.log(output)
mapObj can be written in many different ways. Here are two alternatives:
const mapObj = (f = identity) => (o = {}) =>
Object .fromEntries (Object .entries (o) .map (([ k, v ]) => [ k, f (v) ]))
const mapObj = (f = identity) => (o = {}) =>
Object .assign .apply (null, Object .entries (o) .map (([ k, v ]) => ({ [k]: f (v)
Here's an approach using a modification of the original code to demonstrate what needed to be changed in order to make it work. You had some things switched up reading the value and setting the new one. Also I'm using the spread operator to clone the object before modifying it.
const testObj = {
a: 1,
b: {
c: 2,
d: {
e: 3,
f: {
g: 4
}
}
}
};
const newObject = object => {
const clonedObj = { ...object };
const entries = Object.entries(clonedObj);
entries.forEach(([key, value]) => {
if (typeof value === "object") {
clonedObj[key] = newObject(value);
} else {
clonedObj[key] = value + " a";
}
});
return clonedObj;
};
console.log(newObject(testObj));
console.log(testObj); // prove that the original object hasn't changed
Here's a simple recursive technique. It is similar to Nina's but it preserves arrays, if present in the structure.
If the input, t, is an array, create a new array by traversing each array value, v, with the traversing function, f
(inductive) Otherwise t is not an array. If t is an object, create a new object from key value pairs, [ k, v ], by traversing each value, v, with the traversing function, f
(inductive) Otherwise t is not an array and t is not an object. This means t is either a primitive value, such as string, number, or null
Numbered comments below correspond to the explanation above -
const identity = x =>
x
const traverse = (f = identity, t = {}) =>
Array.isArray(t) // 1
? Array.from(t, v => traverse(f, v))
: Object(t) === t // 2
? Object.fromEntries(Object.entries(t).map(([ k, v ]) => [ k, traverse(f, v) ]))
: f (t) // 3
const input =
{ a: [ 1, 11, 111 ], b: { c: 2, d: { e: [ 3, { f: { g: 4 } } ] } } }
const output =
traverse(x => `${x} a`, input)
console.log(output)
Here is a solution using object-scan. It works by building the solution at the same time as the input is traversed.
// const objectScan = require('object-scan');
const testObj = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4 } } } };
const cloneAndModify = (obj) => objectScan(['**'], {
breakFn: ({ property, value, isLeaf, context }) => {
if (property === undefined) {
return;
}
const ref = context[context.length - 1];
if (!(property in ref)) {
ref[property] = isLeaf ? `${value} a` : {};
}
context.push(ref[property]);
},
filterFn: ({ context }) => {
context.pop();
}
})(obj, [{}])[0];
const r = cloneAndModify(testObj);
console.log(r);
// => { b: { d: { f: { g: '4 a' }, e: '3 a' }, c: '2 a' }, a: '1 a' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan

Categories

Resources