Label-Value object for each object - javascript

I have the following:
const itemsArr = [{
id: 0,
baseDetails: {
modelNames: ["Atoga3"],
companies: ['Sunafga']
}
},
{
id: 1,
baseDetails: {
modelNames: ["Bisuda-X23", "Oidas"],
companies: ["Sunafga", "Kemaite"]
}
},
{
id: 2,
baseDetails: {
modelNames: ["Zarusa-M3", "Kalasi-W"],
companies: ["Abado", "Sunafga"]
}
}
]
What I want to achieve is to have an array with each company -> modelName combination.
So it should look like :
[{
value: 'sunafga',
label: 'Sunafga',
children: [{
value: 'atoga3',
label: 'Atoga3'
},
{
value: 'bisuda-x23',
label: 'bisuda-X23'
},
{
value: 'oidas',
label: 'Oidas'
},
{
value: 'zarusa-m3',
label: 'Zarusa-M3'
},
{
value: 'valasi-W',
label: 'Kalasi-W'
}
]
},
{
value: 'kemaite',
label: 'Kemaite',
children: [{
value: 'bisuda-x23',
label: 'bisuda-X23'
},
{
value: 'oidas',
label: 'Oidas'
},
]
},
]

you probably want a two step transform here, one to a useful structure to remove all those dups, then just nudge that into your desired structure
const itemsArr = [{
id: 0,
baseDetails: {
modelNames: ["Atoga3"],
companies: ['Sunafga']
}
},
{
id: 1,
baseDetails: {
modelNames: ["Bisuda-X23", "Oidas"],
companies: ["Sunafga", "Kemaite"]
}
},
{
id: 2,
baseDetails: {
modelNames: ["Zarusa-M3", "Kalasi-W"],
companies: ["Abado", "Sunafga"]
}
}
]
// transform to a useful structure for removing duplicates
const companyMap = itemsArr.reduce((acc, val) => {
val.baseDetails.companies.forEach(c => {
acc[c] = val.baseDetails.modelNames.reduce((a, m) => Object.assign(a, {[m]: true}), (acc[c] || {}))
});
return acc
}, {})
// transform your useful structure to the desired one
const newArray = Object.entries(companyMap).map(([company, models]) => {
return {
value: company.toLowerCase(),
label: company,
children: Object.keys(models).map(model => ({label: model, value: model.toLowerCase()}))
}
})
console.log(newArray)
you could use this if you want as your intermediary transform if you want to reduce all the way down:
const companyMap = itemsArr.reduce((cMap, item) =>
Object.assign(cMap,
item.baseDetails.companies.reduce((iMap, c) =>
Object.assign(iMap,
{[c]: item.baseDetails.modelNames.reduce((a, m) => Object.assign(a, {[m]: true}), (cMap[c] || {}))}
)
, {})
)
, {})

Here's a solution if using reduce isn't mandatory:
const itemsArr = [{
id: 0,
baseDetails: {
modelNames: ["Atoga3"],
companies: ['Sunafga']
}
},
{
id: 1,
baseDetails: {
modelNames: ["Bisuda-X23", "Oidas"],
companies: ["Sunafga", "Kemaite"]
}
},
{
id: 2,
baseDetails: {
modelNames: ["Zarusa-M3", "Kalasi-W"],
companies: ["Abado", "Sunafga"]
}
}
];
const result = [];
itemsArr.forEach(item => {
item.baseDetails.companies.forEach(company => {
let companyEntry = result.find(resultEntry => resultEntry.label === company);
if (!companyEntry) {
companyEntry = {
label: company,
value: company.toLowerCase(),
children: []
};
result.push(companyEntry);
}
const companyChildren = companyEntry.children;
item.baseDetails.modelNames.forEach(modelName => {
if (!companyChildren.find(companyModel => companyModel.label === modelName)) {
companyChildren.push({
label: modelName,
value: modelName.toLowerCase()
});
}
});
});
});
console.log(result);

Related

How to remove underscore from loop items?

I am trying to remove all the _er and _bx from the array, how can I do it? The way I tried doesn't seem to work. I'd like to see a solution where it removes all after _, and aswell only the letter that I put in for e.g remove all _ with er after.
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
const newArr = []
for (let [key, item] of nullValue.collection.entries()) {
item.name.replace(/_er/g, '')
newArr.push(item)
}
console.log(newArr)
Is this what you're looking for?
const nullValue = {
collection: [
{
name: 'test_er',
},
{
name: 'test_bx',
},
{
name: 'fred',
},
{
name: 'test_er',
},
],
};
nullValue.collection = [
...nullValue.collection.map(item => ({
name: item.name.replace(/_.*$/, ''),
})),
];
console.log(nullValue);
You can also use .split('_')[0] with the map method similar to Dmitry's answer... This gives you the first string of the split array, split at the underscore...
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
nullValue.collection = [ ...nullValue.collection.map( names => ({ name: names.name.split('_')[0], })),]
console.log(nullValue)
If you want to keep the original array of objects...
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
const newArr = { collection :
[ ...nullValue.collection.map( names =>
({ name: names.name.split('_')[0], })),
]}
console.log('newArr = ', newArr)
console.log('nullValue = ', nullValue)
You were VERY close with your original code, but the mistake was that String.replace() does not operate in-place, but rather returns its result. I've modified your code and added a comment below:
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
const newArr = []
for (let [key, item] of nullValue.collection.entries()) {
// My change is here
newArr.push( item.name.replace(/_er/g, '') )
}
console.log(newArr)
const nullValue = {
collection: [
{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
nullValue.collection = nullValue.collection.map(i => i.name.replace(/_.*$/, ''))
console.log(nullValue)
This is preferable to .map() since you don't need a new array. You just want to change the strings:
const nullValue = {
collection: [
{ name: "test_er" },
{ name: "test_bx" },
{ name: "fred" },
{ name: "test_er" }
]
};
nullValue.collection.forEach(i => i.name = i.name.replace(/_.*$/, ''));
console.log(nullValue.collection);

Push into an Array inside an Object in redux?

I have the reducer that contains some of the list objects.
const list = [
{
name: 'A',
products: { items: [] },
},
{
name: 'B',
products: { items: [{ qty: 1 }] },
},
]
I want to add new items to the product key.
reducer
export const addProductToSubscription = (state, { name, products }) => ({
...state,
list: state.list.map((v) =>
name === v.subscriptionName ? [...v.products, { ...v.products, items: products }] : v
),
})
disptach like this,
dispatch("A",[{qty:2}])
expected output
const list = [
{
name: 'A',
products: { items: [{ qty: 2 }] },
},
{
name: 'B',
products: { items: [{ qty: 1 }] },
},
]
What reducer not updating the state.
Thanks!!
I've recreated your environment and end up like-
const state = {
list: [
{
name: "A",
products: { items: [] },
},
{
name: "B",
products: { items: [{ qty: 1 }] },
},
],
};
const updateState = (state, { name, products }) => {
return {
...state,
list: state.list.map(v => {
return {
...v,
products: {
...v.products,
items: [
...v.products.items,
...products
]
}
}
}),
};
};
const res = updateState(state, { name: "A", products: [{ qty: 2 }, {qty: 4}] });
console.log(JSON.stringify(res));
.as-console-wrapper{min-height: 100%!important; top: 0}
A more advanced solution than Object.assign and spread operator(...) - immutability-helper
const update = require('immutability-helper');
const assert = require('assert');
const list = [
{
name: 'A',
products: { items: [] },
},
{
name: 'B',
products: { items: [{ qty: 1 }] },
},
];
function addProductToSubscription(state = list, { name, products }) {
return update(state, {
$apply: (self) => {
return self.map((v) => {
if (v.name === name) {
return update(v, { products: { items: { $push: products } } });
}
return v;
});
},
});
}
const actual = addProductToSubscription(undefined, { name: 'A', products: [{ qty: 2 }] });
console.log(JSON.stringify(actual, null, 2));
assert(actual !== list, 'new copy');
assert(actual[0] !== list[0], 'new copy for A object');
assert(actual[0].products !== list[0].products, 'new copy for A object products');
assert(actual[0].products.items !== list[0].products.items, 'new copy for A object products items');
Output:
[
{
"name": "A",
"products": {
"items": [
{
"qty": 2
}
]
}
},
{
"name": "B",
"products": {
"items": [
{
"qty": 1
}
]
}
}
]
Take the new copy using spread operator and update to the state
export const addProductToSubscription = (state, { name, products }) => ({
...state,
list: state.list.map((v) =>
v.name === name
? {
...v,
products: { items: [...v.products.items, ...products] },
}
: v
),
});

Better way to find data across multiple arrays

getComponentById: (state) => (componentId) => {
return state.articles
.filter(article => Object.keys(article).some(key => {
return ['maps', 'charts', 'tables'].includes(key);
}))
.reduce((acc, article) => {
acc = article.components?.find(c => c.id == componentId);
if (acc) return acc;
acc = article.maps?.find(c => c.id == componentId);
if (acc) return acc;
acc = article.charts?.find(c => c.id == componentId);
if (acc) return acc;
acc = article.tables?.find(c => c.id == componentId);
if (acc) return acc;
})
}
Wonder if there's a better way to rewrite this because the list of components might grow so it feels wrong to just keep adding the lines.
If the id is unique can you just look into every key on every article?
If my guess at your data structure is close you should be able to do something like this
let articles = [
{
maps: [{ id: 1, name: 'map1' }, { id: 2, name: 'map2' }],
charts: [{ id: 3, name: 'charts1' }, { id: 4, name: 'charts2' }],
tables: [{ id: 5, name: 'tables1' }, { id: 6, name: 'tables2' }]
},
{
maps: [{ id: 7, name: 'map3' }, { id: 8, name: 'map4' }],
charts: [{ id: 9, name: 'charts3' }, { id: 10, name: 'charts4' }],
tables: [{ id: 11, name: 'tables3' }, { id: 12, name: 'tables4' }]
}
]
let getComponentById = (componentId) => {
let result = null;
articles.forEach(article => {
Object.keys(article).forEach(key => {
let component = article[key].find(x=> x.id == componentId);
if(component) {
result = component;
}
});
});
return result;
}
console.log(getComponentById(3));
console.log(getComponentById(12));
Credit to #IrKenInvader's answer, I copy data from him.
I use for loop because once you find a component, you can early return and no need to check the rest of the data.
let state = {
articles: [
{
maps: [
{ id: 1, name: "map1" },
{ id: 2, name: "map2" },
],
charts: [
{ id: 3, name: "charts1" },
{ id: 4, name: "charts2" },
],
tables: [
{ id: 5, name: "tables1" },
{ id: 6, name: "tables2" },
],
},
{
maps: [
{ id: 7, name: "map3" },
{ id: 8, name: "map4" },
],
charts: [
{ id: 9, name: "charts3" },
{ id: 10, name: "charts4" },
],
tables: [
{ id: 11, name: "tables3" },
{ id: 12, name: "tables4" },
],
},
],
};
const getComponentById = state => componentId => {
for (let i = 0; i < state.articles.length; i++) {
const filteredKey = Object.keys(state.articles[i]).filter(key =>
["maps", "charts", "tables"].includes(key)
);
for (let j = 0; j < filteredKey.length; j++) {
const foundComponent = state.articles[i][filteredKey[j]].find(
a => a.id == componentId
);
if (foundComponent) return foundComponent;
}
}
return null;
};
const output = getComponentById(state)(12);
console.log(output);

How to merge and return new array from object in es6

Suppose there are two objects.
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
and the result
{
'1-1':[
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
],
'1-2':[
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
],
'2-1':[
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' },
]
}
Basically, I want to group the data.
I use includes to check if the item from b to match the id from a. Then construct the new array.
This is my attempt(fiddle):
return b.map(item => a.map(jtem => {
if(jtem.id.includes(item)){
return {
[item]: jtem
}
}
}))
For somehow, it doesn't work.
and, is there a clever way to avoid the nested for loop or map function?
You can do that in following steps:
Apply reduce() on the array b
During each iteration use filter() on the the array a
Get all the items from a which starts with item of b using String.prototype.startsWith()
At last set it as property of the ac and return ac
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
let res = b.reduce((ac,b) => {
ac[b] = a.filter(x => x.id.startsWith(b));
return ac;
},{})
console.log(res)
As suggested by #Falco is the comments that It would be better to scan over the a once as its large. So here is that version.Actually its better regarding performance
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
let res = a.reduce((ac,x) => {
let temp = b.find(y => x.id.startsWith(y))
if(!ac[temp]) ac[temp] = [];
ac[temp].push(x);
return ac;
},{})
console.log(res)
Note: startsWith is not supported by I.E. So you can create polyfill using indexOf
if(!String.prototype.startWith){
String.prototype.startsWith = function(str){
return this.indexOf(str) === 0
}
}

Merge two array of objects together es6

Currently I have two different array of objects and my end result is I am trying to have one single array of objects.
const postIds = [
{ id: 4938960132 },
{ id: 5586491011 },
{ id: 4671908225 },
{ id: 4594788047 },
{ id: 4657970305 }
]
const images = [
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/rustic20coffee20table.jpeg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Amazing-Table-For-Flamboyant-Furniture-Home-Design-Ideas-With-Rustic-Furniture-Coffee-Table.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/envoy-lookout-rooftop-11b-780x520.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Alexanderplatz_03.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg' }
]
What I am hoping to have at the end is a data structure like the following
const newData = [
{ id: 4938960132, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/rustic20coffee20table.jpeg%3Ft=1528912781831-6.jpeg' },
{ id: 5586491011, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Amazing-Table-For-Flamboyant-Furniture-Home-Design-Ideas-With-Rustic-Furniture-Coffee-Table.jpg%3Ft=1528912781831-6.jpeg' },
{ id: 4671908225, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/envoy-lookout-rooftop-11b-780x520.jpg%3Ft=1528912781831-6.jpeg' },
{ id: 4594788047, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg'},
{ id: 4657970305, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg' }
]
I've been trying a lot of different things here such as reduce, spread operator and other es6 functions but cannot seem to get the data structure that I am looking for.
Any help would be much appreciated
Assuming the two arrays have the same length:
const newData = [...postIds.map((postId, i) => Object.assign({}, postId, images[i]))];
Alternativelly, with ... operator:
const newData = [...postIds.map((item, i) => {
return {
...item,
...images[i]
};
})];
Working snippet:
const postIds = [
{ id: 4938960132 },
{ id: 5586491011 },
{ id: 4671908225 },
{ id: 4594788047 },
{ id: 4657970305 }
]
const images = [
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/rustic20coffee20table.jpeg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Amazing-Table-For-Flamboyant-Furniture-Home-Design-Ideas-With-Rustic-Furniture-Coffee-Table.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/envoy-lookout-rooftop-11b-780x520.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Alexanderplatz_03.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg' }
]
const newData = [...postIds.map((item, i) => Object.assign({}, item, images[i]))];
console.log(newData)
You creduce both array by mapping the objects at the same index and by assigning to a new object.
This works for an arbitrary count of arrays.
const
postIds = [{ id: 4938960132 }, { id: 5586491011 }, { id: 4671908225 }, { id: 4594788047 }, { id: 4657970305 }],
images = [{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/rustic20coffee20table.jpeg%3Ft=1528912781831-6.jpeg' }, { featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Amazing-Table-For-Flamboyant-Furniture-Home-Design-Ideas-With-Rustic-Furniture-Coffee-Table.jpg%3Ft=1528912781831-6.jpeg' }, { featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/envoy-lookout-rooftop-11b-780x520.jpg%3Ft=1528912781831-6.jpeg' }, { featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Alexanderplatz_03.jpg%3Ft=1528912781831-6.jpeg' }, { featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg' }],
result = [images, postIds].reduce(
(r, a) => a.map((o, i) => Object.assign({}, o, r[i])),
[]
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories