Transform object with nested array into a big object - javascript

I need some help to prepare my object properly to update my database
What I get from the API:
{
currency: 'USD',
product_id: '1',
user_id: '123',
service_id: '999',
tech: 'javaScript',
price: '50',
details: [
{ detail_type: 'hours_service', value: '5' },
{ detail_type: 'workers', value: '2' },
{ detail_type: 'machines', value: '2' },
{ detail_type: 'instances', value: '1' },
{ detail_type: 'difficulty', value: '2' },
],
date_start: '2021-06-20',
date_stop: '2021-06-21',
}
What I need to update my DB:
{
currency: 'USD',
product_id: '1',
user_id: '123',
service_id: '999',
tech: 'javaScript',
price: '50',
hours_service: '5',
workers: '2',
machines: '2',
instances: '1',
difficulty: '2',
date_start: '2021-06-20',
date_stop: '2021-06-21',
}
my attempt:
const api_data = {
currency: 'USD',
product_id: '1',
user_id: '123',
service_id: '999',
tech: 'javaScript',
price: '50',
details: [
{ detail_type: 'hours_service', value: '5' },
{ detail_type: 'workers', value: '2' },
{ detail_type: 'machines', value: '2' },
{ detail_type: 'instances', value: '1' },
{ detail_type: 'difficulty', value: '2' },
],
date_start: '2021-06-20',
date_stop: '2021-06-21',
}
for (let item of api_data.details) {
var arr = item
var result = {};
for (var i = 0, len = arr.length; i < len; i++) {
result[arr[i].key] = arr[i].value;
}
console.log(result);
}
The attempt I've made gives me a lot of empty objects...
How can I make the first object detail array transform in key:value pair and make it one big object with all data?
thanks in advance,
Kind regards

You can use reduce to acheive this
const apiData = {
currency: 'USD',
product_id: '1',
user_id: '123',
service_id: '999',
tech: 'javaScript',
price: '50',
details: [
{ detail_type: 'hours_service', value: '5' },
{ detail_type: 'workers', value: '2' },
{ detail_type: 'machines', value: '2' },
{ detail_type: 'instances', value: '1' },
{ detail_type: 'difficulty', value: '2' },
],
date_start: '2021-06-20',
date_stop: '2021-06-21',
};
const {details,...rest} = apiData;
const transformedObj = details.reduce((result,item)=>({...result,[item.detail_type]:item.value}),{});
const result = {...rest,...transformedObj};
console.log(result);

Related

How do I create an array of objects with a nested array based on a similar key?

I have an array that looks something like this
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
As you can see, the organization name is something I want to key off of and create a data structure like this:
const output = [
// data.value will be their ID
{
organizationName: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' },
],
},
{
organizationName: 'Organization B',
data: [
{ label: 'Person 3', value: '3' },
],
},
]
What I've tried
I know I want to use reduce for something like this, but I feel like I'm off:
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
if (organizationName) {
acc.push({ organization: organizationName, data: [] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ name: externalPersonName, value: id });
}
return acc;
}, [] as any);
However the output comes out to something like this:
[
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization B', data: []},
];
data doesn't seem to get anything pushed inside the array in this reduce function, and the organization name get duplicated... what am I doing wrong?
Easiest way is to use an Map/Set/or object to keep track of orgs you create. This way you are not searching in the array to see if the organization was found already. After you are done, you can create the array you want from the object.
const externalPeople = {
data : [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
],
};
const providerOptions = Object.values(externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
name: externalPersonName,
id
} = currentValue;
// Is the org new? Yes, create an entry for it
if (!acc[organizationName]) {
acc[organizationName] = { organization: organizationName, data: [] };
}
// push the person to the organization
acc[organizationName].data.push({ name: externalPersonName, value: id });
return acc;
}, {}));
console.log(providerOptions)
Here is another solution
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const result = example.reduce((res, entry) => {
const recordIndex = res.findIndex(rec => rec.organizationName === entry.organization.name);
if(recordIndex >= 0) {
res[recordIndex].data.push({ label: entry.name, value: entry.id});
} else {
const record = {
organizationName: entry.organization.name,
data: [{ label: entry.name, value: entry.id }]
};
res.push(record);
}
return res;
}, []);
console.log(result);
You are not checking if the value is already present in your accumulation acc
You can check it with a simple find in the if statement since it's an array
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
//Check if organization is not present already
if (!acc.find(a => a.organization === organizationName)) {
//Add also the data of the element your are processing
acc.push({ organization: organizationName, data: [{label: currentValue.name, value: currentValue.id}] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ label: externalPersonName, value: id });
}
return acc;
}, [] as any);
I also added the data of the first element of the group you create when adding the organization.
The result should be as your expected output:
[
{
organization: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' }
]
},
{
organization: 'Organization B',
data: [
{ label: 'Person 3', value: '3' }
]
}
]
Hope it helps!
Compare this solution (using Lodash) with other solutions. Which one emphasises your intentions at most? This is why we use Lodash in our company - to maintain code as declarative as we can, because code readability, with minimum cognitive overload, is most important goal during coding.
const persons = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const personsByOrganizations = _.groupBy(persons, 'organization.name')
const output = _.map(personsByOrganizations, (persons, organizationName) => ({
organizationName,
data: _.map(persons, ({ name, id }) => ({
label: name,
value: id
}))
}))
Something like that with using a Set?
result = [...new Set(example.map(d => d.organization.name))].map(label => {
return {
organizationName: label,
data: example.filter(d => d.organization.name === label).map(d => {
return {label: d.name, value: d.id}
})
}
})
`

How to group by array of objects

I have an array of objects like the below:
const data = [{label: 'ABC', id: '1', emp:{empLabel: 'Test1', empId: '12'}},
{label: 'ABC', id: '1', emp:{empLabel: 'Test2', empId: '13'}},
{label: 'DEF', id: '2', emp:{empLabel: 'Test11', empId: '14'}},
{label: 'DEF', id: '2', emp:{empLabel: 'Test12', empId: '15'}},
{label: 'PQR', id: '3', emp:{empLabel: 'Test13', empId: '16'}},
{label: 'XYZ', id: '4', emp:{empLabel: 'Test14', empId: '17'}}
]
I am trying to club the emp data if my id is equal.
Expected Output:
[
{label: 'ABC', id: '1', emp:[{empLabel: 'Test1', empId: '12'}, {empLabel: 'Test2', empId: '13'}]},
{label: 'DEF', id: '2', emp:[{empLabel: 'Test11', empId: '14'}, {empLabel: 'Test12', empId: '15'}]},
{label: 'PQR', id: '3', emp:{empLabel: 'Test13', empId: '16'}},
{label: 'XYZ', id: '4', emp:{empLabel: 'Test14', empId: '17'}}
]
I have tried to do this by lodash but am not sure how to proceed after this. Any help would appreciate?
My Approach:
result = _.map(data, eachData => {
return _.chain(_.flatMap(eachData))
})
const _ = require("lodash")
let items = [
{ label: 'ABC', id: '1', emp: { empLabel: 'Test1', empId: '12' } },
{ label: 'ABC', id: '1', emp: { empLabel: 'Test2', empId: '13' } },
{ label: 'DEF', id: '2', emp: { empLabel: 'Test11', empId: '14' } },
{ label: 'DEF', id: '2', emp: { empLabel: 'Test12', empId: '15' } },
{ label: 'PQR', id: '3', emp: { empLabel: 'Test13', empId: '16' } },
{ label: 'XYZ', id: '4', emp: { empLabel: 'Test14', empId: '17' } }
]
var result = _(items)
.groupBy('id')
.map(function(items, label) {
return {
label: label,
emp: _.map(items, 'emp')
};
}).value();
console.log("result -> ", result)
This works, unless you are specifically trying to use lodash:
const result = data.reduce((acc, val) => {
const existingGroup = acc.find((group) => val.id === group.id);
if(!!existingGroup) {
if(existingGroup.emp && Array.isArray(existingGroup.emp)) {
existingGroup.emp = [...existingGroup.emp, val.emp];
} else {
existingGroup.emp = [existingGroup.emp, val.emp]
}
} else {
acc = [...acc, val];
}
return acc;
},[]);
console.log(result);
Try using array.filter(). Do something like
ar = []
let newarr = []
data.map(x => if (ar.indexOf(x)===-1) {newarr.push(x); at.push(x.id))
newArr is now your array.

Returning array object by filtering it's nested size variants

I have the list of products that i want to filter by their varriants.size
Staring point, data I'm receiving:
const t1 = [
{
name: 'Product 1',
variants: [
{ size: 'sm', sku: '1' },
{ size: 'md', sku: '2' },
],
},
{
name: 'Product 2',
variants: [{ size: 'lg', sku: '4' }],
},
{
name: 'Product 3',
variants: [
{ size: 'sm', sku: '5' },
{ size: 'lg', sku: '6' },
],
},
{
name: 'Product 4',
variants: [{ size: 'sm', sku: '7' }],
},
]
By using ['sm', 'md'] I want to filter above object and return this result
End goal / expected results
const arr = [
{
name: 'Product 2',
variants: [{ size: 'lg', sku: '4' }],
},
{
name: 'Product 3',
variants: [{ size: 'lg', sku: '6' }],
},
]
What I've tried so far but not getting full data / missing properties.
const filter = ['sm', 'md']
const arr = t1.map((e) => {
const filter = e.variants.filter((f) => {
return filter.includes(f.size)
})
return filter
})
But only getting varriants object, rest of the data is missing.
This screenshot is bad example, this one is only filtering ['sm'] but in this case I have multiple filter option ['sm', 'md']
const
t1 = [
{ name: 'Product 1', variants: [{ size: 'sm', sku: '1' }, { size: 'md', sku: '2' }] },
{ name: 'Product 2', variants: [{ size: 'lg', sku: '4' }] },
{ name: 'Product 3', variants: [{ size: 'sm', sku: '5' }, { size: 'lg', sku: '6' }] },
{ name: 'Product 4', variants: [{ size: 'sm', sku: '7' }] }
],
filter = ['sm', 'md'];
const arr = t1
// filter t1 elements variants
.map(e => ({
...e,
variants: e.variants.filter(({ size }) => !filter.includes(size))
}))
// filter resulting elements with no variants left
.filter(({ variants }) => variants.length);
console.log(arr);
Filter each variants subarray by whether the size you want is included, then filter the whole t1 array by whether the subarray contains items.
const t1 = [
{
name: 'Product 1',
variants: [
{ size: 'sm', sku: '1' },
{ size: 'md', sku: '2' },
],
},
{
name: 'Product 2',
variants: [{ size: 'lg', sku: '4' }],
},
{
name: 'Product 3',
variants: [
{ size: 'sm', sku: '5' },
{ size: 'lg', sku: '6' },
],
},
{
name: 'Product 4',
variants: [{ size: 'sm', sku: '7' }],
},
];
const filterBy = ['sm', 'md'];
for (const obj of t1) {
obj.variants = obj.variants.filter(
subobj => !filterBy.includes(subobj.size)
);
}
const filteredInput = t1.filter(obj => obj.variants.length);
console.log(filteredInput);

How to return the array of intersection between two array with Lodash?

I am trying to return the array of all the intersected array elements.
I got 2 arrays.
The array from api and the filter condition array.
Array from api is this
let somethingList = [
{
id: 'PROD108',
name: 'Headsweats Mid Cap',
CustomFields: [
{
name: 'Brand',
value: 'Headsweats',
},
{
name: 'Eco',
value: 'False',
},
{
name: 'Test',
value: '0',
},
],
},
{
id: 'PROD109',
name: 'Performance Liberty City Cycling Cap',
CustomFields: [
{
name: 'Brand',
value: 'Performance',
},
{
name: 'Eco',
value: 'False',
},
{
name: 'Test',
value: '0',
},
],
},
{
id: 'PROD110',
name: 'Castelli Logo Bandana',
CustomFields: [
{
name: 'Brand',
value: 'Castelli',
},
{
name: 'Eco',
value: 'False',
},
{
name: 'Test',
value: '0',
},
],
},
{
id: 'PROD159',
name: 'Performance Classic Sleeveless Jersey',
CustomFields: [
{
name: 'Eco',
value: 'False',
},
{
name: 'Color',
value: '#4CAF50',
},
{
name: 'Test',
value: '0',
},
],
},
{
id: 'PROD160',
name: 'Schwinn Evolution IC Sleeveless Jersey',
CustomFields: [
{
name: 'Brand',
value: 'Schwinn',
},
{
name: 'Eco',
value: 'False',
},
{
name: 'Color',
value: '#2196F3',
},
{
name: 'Test',
value: '0',
},
],
},
{
id: 'PROD161',
name: 'Performance Elite Short',
CustomFields: [
{
name: 'Brand',
value: 'Performance',
},
{
name: 'Eco',
value: 'False',
},
{
name: 'Color',
value: '#000000',
},
{
name: 'Test',
value: '0',
},
],
},
{
id: 'PROD162',
name: 'Andiamo! Padded Cycling Brief',
CustomFields: [
{
name: 'Eco',
value: 'False',
},
{
name: 'Color',
value: '#808080',
},
{
name: 'Test',
value: '0',
},
],
},
{
id: 'PROD163',
name: 'Fox Mojave Glove',
CustomFields: [
{
name: 'Brand',
value: 'Fox',
},
{
name: 'Eco',
value: 'False',
},
{
name: 'Color',
value: '#000000',
},
{
name: 'Test',
value: '0',
},
],
},
];
filter condition array.
let testingFilter = ['Fox', 'Performance'];
What I want to do is if the customfield value of array from api is intersected with testingFilter value
I want to push them into an array and return that new array.
But the code I written don't return an new array, What should I do to return a new array
let filteredProduct = [];
filteredProduct = _.filter(somethingList, (product) => {
if (testingFilter.length === 0) {
return somethingList;
} else {
// Here is the problem
return _.intersection(testingFilter, _.map(product.CustomFields, 'value'));
}
});
Expected Answer array
filteredProduct = [
{
id: 'PROD109',
name: 'Performance Liberty City Cycling Cap',
CustomFields: [
{
name: 'Brand',
value: 'Performance',
},
{
name: 'Eco',
value: 'False',
},
{
name: 'Test',
value: '0',
},
],
},
{
id: 'PROD161',
name: 'Performance Elite Short',
CustomFields: [
{
name: 'Brand',
value: 'Performance',
},
{
name: 'Eco',
value: 'False',
},
{
name: 'Color',
value: '#000000',
},
{
name: 'Test',
value: '0',
},
],
},
{
id: 'PROD163',
name: 'Fox Mojave Glove',
CustomFields: [
{
name: 'Brand',
value: 'Fox',
},
{
name: 'Eco',
value: 'False',
},
{
name: 'Color',
value: '#000000',
},
{
name: 'Test',
value: '0',
},
],
},
]
The following line taken from your code:
_.map(product.CustomFields, 'value')
Here, you get an array of all the values's of the CustomFields, if we check if there is an item from testinFilter present in that array, we can use that as the _filter return statement like so:
let filteredProduct = [];
filteredProduct = _.filter(somethingList, (product) => {
const allCustomFields = _.map(product.CustomFields, 'value');
return allCustomFields.some(r => testingFilter.indexOf(r) >= 0);
});
// We could rewrite the same code as a one-liner without the extra const like so:
let filteredProduct = _.filter(somethingList, (product) => _.map(product.CustomFields, 'value').some(r => testingFilter.indexOf(r) >= 0));
Snippet:
let testingFilter = ['Fox', 'Performance'];
let somethingList = [{id: 'PROD108', name: 'Headsweats Mid Cap', CustomFields: [{name: 'Brand', value: 'Headsweats', }, {name: 'Eco', value: 'False', }, {name: 'Test', value: '0', }, ], }, {id: 'PROD109', name: 'Performance Liberty City Cycling Cap', CustomFields: [{name: 'Brand', value: 'Performance', }, {name: 'Eco', value: 'False', }, {name: 'Test', value: '0', }, ], }, {id: 'PROD110', name: 'Castelli Logo Bandana', CustomFields: [{name: 'Brand', value: 'Castelli', }, {name: 'Eco', value: 'False', }, {name: 'Test', value: '0', }, ], }, {id: 'PROD159', name: 'Performance Classic Sleeveless Jersey', CustomFields: [{name: 'Eco', value: 'False', }, {name: 'Color', value: '#4CAF50', }, {name: 'Test', value: '0', }, ], }, {id: 'PROD160', name: 'Schwinn Evolution IC Sleeveless Jersey', CustomFields: [{name: 'Brand', value: 'Schwinn', }, {name: 'Eco', value: 'False', }, {name: 'Color', value: '#2196F3', }, {name: 'Test', value: '0', }, ], }, {id: 'PROD161', name: 'Performance Elite Short', CustomFields: [{name: 'Brand', value: 'Performance', }, {name: 'Eco', value: 'False', }, {name: 'Color', value: '#000000', }, {name: 'Test', value: '0', }, ], }, {id: 'PROD162', name: 'Andiamo! Padded Cycling Brief', CustomFields: [{name: 'Eco', value: 'False', }, {name: 'Color', value: '#808080', }, {name: 'Test', value: '0', }, ], }, {id: 'PROD163', name: 'Fox Mojave Glove', CustomFields: [{name: 'Brand', value: 'Fox', }, {name: 'Eco', value: 'False', }, {name: 'Color', value: '#000000', }, {name: 'Test', value: '0', }, ], }, ];
let filteredProduct = _.filter(somethingList, (product) => {
const allCustomFields = _.map(product.CustomFields, 'value');
return allCustomFields.some(r => testingFilter.indexOf(r) >= 0);
});
console.log(filteredProduct);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
Result:
[
{
"id": "PROD109",
"name": "Performance Liberty City Cycling Cap",
"CustomFields": [
{
"name": "Brand",
"value": "Performance"
},
{
"name": "Eco",
"value": "False"
},
{
"name": "Test",
"value": "0"
}
]
},
{
"id": "PROD161",
"name": "Performance Elite Short",
"CustomFields": [
{
"name": "Brand",
"value": "Performance"
},
{
"name": "Eco",
"value": "False"
},
{
"name": "Color",
"value": "#000000"
},
{
"name": "Test",
"value": "0"
}
]
},
{
"id": "PROD163",
"name": "Fox Mojave Glove",
"CustomFields": [
{
"name": "Brand",
"value": "Fox"
},
{
"name": "Eco",
"value": "False"
},
{
"name": "Color",
"value": "#000000"
},
{
"name": "Test",
"value": "0"
}
]
}
]
You can do:
let somethingList = [ { id: 'PROD108', name: 'Headsweats Mid Cap', CustomFields: [ { name: 'Brand', value: 'Headsweats', }, { name: 'Eco', value: 'False', }, { name: 'Test', value: '0', }, ], }, { id: 'PROD109', name: 'Performance Liberty City Cycling Cap', CustomFields: [ { name: 'Brand', value: 'Performance', }, { name: 'Eco', value: 'False', }, { name: 'Test', value: '0', }, ], }, { id: 'PROD110', name: 'Castelli Logo Bandana', CustomFields: [ { name: 'Brand', value: 'Castelli', }, { name: 'Eco', value: 'False', }, { name: 'Test', value: '0', }, ], }, { id: 'PROD159', name: 'Performance Classic Sleeveless Jersey', CustomFields: [ { name: 'Eco', value: 'False', }, { name: 'Color', value: '#4CAF50', }, { name: 'Test', value: '0', }, ], }, { id: 'PROD160', name: 'Schwinn Evolution IC Sleeveless Jersey', CustomFields: [ { name: 'Brand', value: 'Schwinn', }, { name: 'Eco', value: 'False', }, { name: 'Color', value: '#2196F3', }, { name: 'Test', value: '0', }, ], }, { id: 'PROD161', name: 'Performance Elite Short', CustomFields: [ { name: 'Brand', value: 'Performance', }, { name: 'Eco', value: 'False', }, { name: 'Color', value: '#000000', }, { name: 'Test', value: '0', }, ], }, { id: 'PROD162', name: 'Andiamo! Padded Cycling Brief', CustomFields: [ { name: 'Eco', value: 'False', }, { name: 'Color', value: '#808080', }, { name: 'Test', value: '0', }, ], }, { id: 'PROD163', name: 'Fox Mojave Glove', CustomFields: [ { name: 'Brand', value: 'Fox', }, { name: 'Eco', value: 'False', }, { name: 'Color', value: '#000000', }, { name: 'Test', value: '0', }, ], }, ];
let testingFilter = ['Fox', 'Performance'];
let filteredProducts = somethingList.filter(p =>
Array.from(p.CustomFields.values()) // iterable to array
.map(({value}) => value)
.some(value => testingFilter.includes(value)))
console.log(filteredProducts)

Filter two array of objects and get a resultant array

I'm trying to check for condition where carId in one array is equal to id of another array.
Below is the code snippet.
const arr1 = [{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '2',
type: 'car',
name: 'Audi',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
const arr2 = [{
carId: '1'
}, {
carId: '3'
}];
const result = arr2.map(val => arr2.find(val.carId === id))
console.log(result)
The result that I'm expecting is
[{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
Could anyone please help?
While you should use .filter() on arr1, and pass a callback to .find(), I'd probably first convert arr2 to a simple list of IDs and use .includes() instead.
const arr1 = [{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '2',
type: 'car',
name: 'Audi',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
const arr2 = [{
carId: '1'
}, {
carId: '3'
}];
const ids = arr2.map(o => o.carId);
const result = arr1.filter(val => ids.includes(val.id))
console.log(result)
or better yet, convert arr2 to a Set.
const arr1 = [{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '2',
type: 'car',
name: 'Audi',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
const arr2 = [{
carId: '1'
}, {
carId: '3'
}];
const ids = arr2.map(o => o.carId);
const idSet = new Set(ids);
const result = arr1.filter(val => idSet.has(val.id))
console.log(result)
const arr1 = [{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '2',
type: 'car',
name: 'Audi',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
const arr2 = [{
carId: '1'
}, {
carId: '3'
}];
const result = arr1.filter(a1val => arr2.find(a2val => a2val.carId === a1val.id) !== undefined);
console.log(result);
This might work
const result = arr2.map(val => arr1.find(item => item.id === val.carId))

Categories