javascript/lodash: cleaner way to spread properties? - javascript

I have a piece of data below. It's an object that contains 2 nested objects with arrays nested in those:
let data = {
obj1: {
names: ['joe'],
ages: false,
},
obj2: {
names: ['james'],
ages: true,
},
}
I want to return:
{
names: ['james','joe'],
ages: true,
}
Right now, I am doing this with:
const foo = Object.entries(data)[0][1] ?? [];
const foo2 = Object.entries(data)?.[1]?.[1] ?? [];
const finalData = {...foo, ...foo2 }
how can I clean that up using loDash's groupBy?

Use _.values() to get the two sub-objects, and then merge them using _.mergeWith(). If the values are an array, concat them, if not let _.mergeWith() handle the merge by returning undefined:
const data = {
obj1: {
names: ['joe', 'james'],
ages: false,
},
obj2: {
names: ['james'],
ages: true,
},
}
const result = _.mergeWith(
{}, ..._.values(data),
(a, b) => _.isArray(a) ? _.uniq([...a, ...b]) : undefined
)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous"></script>

Related

How to add elements in an object while traversing through an array using map?

I am having an array like this :
arr = [ {id:0,name:Mark} , {id:1,name:Ron}, {id:2,name:Henry}, {id:3,name:Rose}].
I want to create an object like this :
obj1 = { Mark:false, Ron:false, Henry:false, Rose:flase }
I am using map to traverse through the array like this
let obj1 = {};
obj1 = arr.map((item)=> {
obj1[item.name] = false;
})
How can I achieve the following result?
You could map entries and build an object from the pairs.
const
data = [{ id: 0, name: 'Mark' }, { id: 1, name: 'Ron' }, { id: 2, name: 'Henry' }, { id: 3, name: 'Rose' }],
result = Object.fromEntries(data.map(({ name }) => [name, false]));
console.log(result);
Object.fromEntries() is probably the best idea. But you could also use reduce, if you got more operations on the array and want to stick to the "pipe" approach.
const arr = [
{ id: 0, name: 'Mark' },
{ id: 1, name: 'Ron' },
{ id: 2, name: 'Henry' },
{ id: 3, name: 'Rose' }
];
const objA = arr
.reduce((previous, { name }) => ({ ...previous, [name]: false }), {});
const objB = arr
.reduce((previous, { name }) => {
previous[name] = false;
return previous;
}, {});
The spreach operation {...obj} for objA does effectivly copy the object on each extension, which might not be desirable. But the modern JavaScript engines simplify those expressions anyways.
objB is the more standard approach for me. One additional benefit, in regards to Object.fromEntries() is that you can have some form of standard or default object / settings, which you can use as the start of the reduce (the second parameter) and evaluate in the collection function.
All three options are valid and depend on your code style.

Combining 2 objects through a loop

let response = {
data1: {
names: ["John", "jim"]
},
data2: {
ages: [34, 24]
}
}
I want:
data: {
names: ["John", "jim"],
ages: [34,24]
}
Is there a way to merge these into 1 object without referencing response.data1 and response.data2? I know I can do const newObj = {...response.data1, ...response.data2} or Object.assign({}, response.data1, response.data1)... Can I do via a for in loop?
You could merge the values of the object using Object.assign() like this:
Object.assign({}, ...Object.values(response))
Explanation:
This is the syntax for Object.assign: Object.assign(target, source1, source2, ... etc). It basically takes all the properties from the source objects and updates the target object
The Object.values() bit returns the value of the response object. In this case, it's an array of objects like this:
[
{ names: ["John", "jim"] },
{ ages: [34, 24] }
]
You can spread the array and pass individual object as a parameter to Object.assign()
Here's a snippet:
let response = {
data1: {
names: ["John", "jim"]
},
data2: {
ages: [34, 24]
}
}
const output = Object.assign({}, ...Object.values(response))
console.log(output)
If it's too confusing you could use reduce:
Object.values(response)
.reduce((a, v) => ({ ...a, ...v }), {})
Or a simple loop:
const output = {}
for(const key in response)
Object.assign(output, response[key])
If you want to merge data1 and data2 using a for in loop, try this:
let response = {
data1: {
names: ["John", "jim"]
},
data2: {
ages: [34, 24]
}
}
var result = {}
for (data in response) {
if (response.hasOwnProperty(data)) {
[key] = Object.keys(response[data])
result[key] = response[data][key]
}
}
console.log(result)
The way it works is by getting the keys of data and data2, then using those keys, it gets the values and adds it to result

How to convert an array of objects back to one object

I got an array of objects that has the following structure:
const cars = {
ford: {
entries: ['ford 150']
},
chrysler: {
entries: ['Voyager']
},
honda: {
entries: []
},
toyota: {
entries: ['sienna']
},
mercedes: {
entries: []
},
}
For the user to be able to rearrange the order of the cars, I need to filter out the car brands that have zero entries, so I do this:
const filteredCars = Object.values(cars).filter(removeCarsWithNoEntries)
function removeCarsWithNoEntries(e) {
if (e.entries[0]) return e.entries
}
Then, once the order is rearranged, I have to convert the object back to its original form, but with the order rearranged, meaning something like this:
cars = {
toyota: {
entries: ['sienna']
},
chrysler: {
entries: ['Voyager']
},
ford: {
entries: ['ford 150']
},
honda: {
entries: []
},
mercedes: {
entries: []
},
}
I've read upon array.reduce and Object.assign but so far the things I have tried do not work.
How can I convert the array of objects back to one single object with the original car brands that had 0 entries? Thanks.
You can convert the object to entries via Object.entries(), and then filter/sort by the value (the 2nd item in the pair), and convert back to an object using Object.fromEntries():
const cars = {"ford":{"entries":["ford 150"]},"chrysler":{"entries":["Voyager"]},"honda":{"entries":[]},"toyota":{"entries":["sienna"]},"mercedes":{"entries":[]}}
const sorted = Object.fromEntries(
Object.entries(cars)
.sort(([, { entries: a }], [, { entries: b }]) => b.length - a.length)
)
const filtered = Object.fromEntries(
Object.entries(cars)
.filter(([, v]) => v.entries.length)
)
console.log({ sorted, filtered })
Checking your code... the filter method doesn't works because return an array...
do you need use length property to check if has values, otherwise it will always be true
const cars = {
ford: {
entries: ['ford 150']
},
chrysler: {
entries: ['Voyager']
},
honda: {
entries: []
},
toyota: {
entries: ['sienna']
},
mercedes: {
entries: []
},
}
filtered = {}
Object.keys(cars).forEach(removeCarsWithNoEntries)
function removeCarsWithNoEntries(brand) {
if(!cars[brand].entries.length) return
filtered[brand] = cars[brand]
}
console.log(filtered)

How to consolidate an array of multiple objects into single object?

So, I have an array like this:
[
{ tags__region: "Stockholm" },
{ tags__region: "Lund" },
{ tags__region: "Mora" },
{ tags__user: "Johan" },
{ tags__user: "Eva" }
]
and I want to turn that into an object like this:
{
tags__region: ["Stockholm", "Lund", "Mora"],
tags__user: ["Johan", "Eva"]
}
Is there a way with lodash?
Are vanilla Array/Object -methods simple enough?
Keep in mind the keys on my array are unknown, so they are not always the same.
Simple Javascript.
let arr = [{
tags__region: "Stockholm"
},
{
tags__region: "Lund"
},
{
tags__region: "Mora"
},
{
tags__user: "Johan"
},
{
tags__user: "Eva"
}
];
arr = arr.reduce((acc, val) => {
let key = Object.keys(val)[0];
let value = Object.values(val)[0];
acc[key] = acc[key] ? [...acc[key],value] : [value]
return acc;
}, {})
console.log(arr);
You can use Lodash's _.mergeWith() with array spread to combine all items in the array to a single object. If the same property exists in two object, the values will be collected to an array:
const arr = [{"tags__region":"Stockholm"},{"tags__region":"Lund"},{"tags__region":"Mora"},{"tags__user":"Johan"},{"tags__user":"Eva"}]
const result = _.mergeWith({}, ...arr, (objValue = [], srcValue) =>
[...objValue, srcValue]
)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
With Lodash/fp you can generate a function (fn) using _.mergeAllWith(), and _.concat() that will do the same thing:
const fn = _.mergeAllWith(_.concat)
const arr = [{"tags__region":"Stockholm"},{"tags__region":"Lund"},{"tags__region":"Mora"},{"tags__user":"Johan"},{"tags__user":"Eva"}]
const result = fn(arr)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>

changing an array of javascript objects with a single key to an array that contains the objects' data

I currently have data in the following format:
var anArray = [
obj1: {
key1: data1
},
obj2: {
key2: data2
},
];
I would like the data to instead be in the following format:
var array2 = [data1, data2];
for some reason, I cannot figure out a concise way to to this. I know it could be done with a forEach loop that iterates over each object and pushes it onto a new array, but I would prefer to be more elegant (and shorter if possible) than that.
const anArray = {
obj1: {
key1: "A"
},
obj2: {
key2: "B"
},
};
const result = Object.keys(anArray).map(key => {
const obj = anArray[key];
return Object.keys(obj).map(key => obj[key])[0];
});
console.log(result);
Given that anArray is actually properly structured to be valid, then you could do the following:
Note that in this case anArray isn't an actual array but rather a object literal
var anArray = {
obj1: {
key1: "data1"
},
obj2: {
key2: "data2"
},
};
var array2 = []
for(i in anArray){
for(j in anArray[i]){
array2.push(anArray[i][j])
}
}
console.log(array2)
https://jsfiddle.net/wh4r0w5s/
Try with:
const arr1 = [
{key1:'value1'},
{key2:'value2'}
]
const res = arr1.map(obj => {
return Object.keys(obj).map(val => obj[val])
}).reduce((acc,v) => {
return acc.concat(v);
},[]);
console.log(res);
update
But if you have the following form:
var anArray = [
obj1: {
key1: data1
},
obj2: {
key2: data2
},
];
It's better to apply a recursive function, as follow:
const arr1 = [
{
obj1:{key1:'value1',key3:'value3'}
},
{
obj2:{key2:'value2'}
}
]
const getValuesFromObj = (obj) => {
if(typeof obj === 'string')
return obj;
return Object.keys(obj).map(key => {
return getValuesFromObj(obj[key]);
}).reduce((acc,v) => {
return acc.concat(v);
},[]);
}
const r2 = getValuesFromObj(arr1);
console.log(r2);

Categories

Resources