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);
Related
I have a list look like:
const initArray = [
{
id: 0,
},
{
id: 1,
},
{
id: 2,
},
{
id: 3,
},
];
A selected list look like:
const selectedList = [
{
id: 2,
},
];
And the desired data has been sorted:
const outPut= [
{
id: 2,
},
{
id: 0,
},
{
id: 1,
},
{
id: 3,
},
];
I'm in trouble right now, so I can't figure it out yet.
Can you share some solutions?
You could take an object which keeps the order of the first objects and sort the rest after.
const
data = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }],
selectedList = [{ id: 2 }],
order = Object.fromEntries(selectedList.map(({ id }, i) => [id, i + 1]));
data.sort((a, b) => (order[a.id] || Number.MAX_VALUE) - (order[b.id] || Number.MAX_VALUE));
console.log(data);
Using Set and Array#map, get set of ids to prioritize
Using Array#sort, sort the items using the above set
const _sort = (arr = [], selected = []) => {
const priority = new Set( selected.map(({ id }) => id) );
return [...arr].sort(({ id: a }, { id: b }) => priority.has(b) - priority.has(a));
}
const
initArray = [ { id: 0 }, { id: 1 }, { id: 2 }, { id: 3 } ],
selectedList = [ { id: 2 } ];
console.log( _sort(initArray, selectedList) );
<script> var itemsTemp= [
{ id: 0, text: 'Andy' },
{
id: 1, text: 'Harry',
children: [
{ id: 2, text: 'David' }
]
},
{ id: 3, text: 'Lisa' },
{ id: 4, text: 'Mona' },
{ id: 5, text: 'Ron' },
{ id: 6, text: 'Joe' }
];
var items = itemsTemp;
var filtered = items.filter(function(item) {
return item.id !== 3;
});
console.log(filtered);
</script>
in this way, I can only remove the parent but how can I delete the child object? please help me to fix this
Since you want to filter children, you can use .reduce() to perform a mapping and filtering of your array. When you reach an object which has a children property, you can recursively call your function to then perform the mapping/filtering on the child array .reduce() array like so:
const items = [{ id: 0, text: 'Andy' }, { id: 1, text: 'Harry', children: [{ id: 2, text: 'David' }] }, { id: 3, text: 'Lisa' }, { id: 4, text: 'Mona' }, { id: 5, text: 'Ron' }, { id: 6, text: 'Joe' } ];
const filterItems = (items, fn) => items.reduce((acc, item) => {
if(item.children)
return [...acc, ...filterItems(item.children, fn)];
else if(fn(item))
return [...acc, item];
return acc;
}, []);
const filtered = filterItems(items, item => item.id !== 2);
console.log(filtered);
If you don't want to remove the item from the parent list, and only from the child list, then you push an update object instead:
const items = [{ id: 0, text: 'Andy' }, { id: 1, text: 'Harry', children: [{ id: 2, text: 'David' }] }, { id: 3, text: 'Lisa' }, { id: 4, text: 'Mona' }, { id: 5, text: 'Ron' }, { id: 6, text: 'Joe' } ];
const toRemoveId = 2;
const filterItems = (items, fn) => items.reduce((acc, item) => {
if(item.children)
return [...acc, {...item, children: filterItems(item.children, fn)}];
else if(fn(item))
return [...acc, item];
return acc;
}, []);
const filtered = filterItems(items, item => item.id !== 2);
console.log(filtered);
This will work for arbitrary object depths.
I just wrote the filterById function I think it works for your case
var itemsTemp = [
{ id: 0, text: "Andy" },
{
id: 1,
text: "Harry",
children: [{ id: 2, text: "David" }],
},
{ id: 3, text: "Lisa" },
{ id: 4, text: "Mona" },
{ id: 5, text: "Ron" },
{ id: 6, text: "Joe" },
];
var items = itemsTemp;
const filterById = (items, id) => {
return items.reduce((accumulator, currentValue) => {
if(currentValue.children){
const newCurrentValue = filterById(currentValue.children, id)
currentValue = {...currentValue, children: newCurrentValue}
}
if(currentValue.id !== id){
return [...accumulator, currentValue]
}
return accumulator
},[])
}
console.log(filterById(itemsTemp,2));
console.log(itemsTemp)
I think you can do like this.
var itemsTemp= [
{ id: 0, text: 'Andy' },
{
id: 1, text: 'Harry',
children: [
{ id: 2, text: 'David' }
]
},
{ id: 3, text: 'Lisa' },
{ id: 4, text: 'Mona' },
{ id: 5, text: 'Ron' },
{ id: 6, text: 'Joe' }
];
var items = itemsTemp;
var filtered = items.filter(function(item) {
childrens=item.children;
if(childrens)
{
filteredchildren = childrens.filter(children=>children.id!==2);
item.children=filteredchildren;
}
return item.id !== 2;
});
console.log(filtered);
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);
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
}
}
I have a Object which looks like the following obj.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
];
The Object should be converted into the following scheme:
var items = [
{ id: 11, name: "fruits", items: [
{ id: 32, name: "banana" }
]},
{ id: 10, name: "threes" },
{ id: 1, name: "animals", items: [
{ id: 2, name: "cat" },
{ id: 3, name: "dog" },
{ id: 4, name: "weazle", items: [
{ id: 5, name: "sand shadow weazle" }
]}
]},
{ id: 15, name: "cars" }
];
I tried a lot but unfortunately without any success. I did $.each on obj, did a split('_') on it and pushed it to items. But how can I do it for unlimited depth and push it into the right category?
I'm happy for any help.
Maybe this helps.
It works with Array.prototype.forEach for processing obj, Array.prototype.reduce for getting the right branch and Array.prototype.some for the right array element for inserting the new object.
This proposal works for sorted and consistent data.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
],
tree = [];
obj.forEach(function (a) {
var path = a.name.split('_'),
o = {};
o.id = a.id;
path.reduce(function (r, b) {
o.name = b;
r.some(function (c) {
if (c.name === b) {
c.items = c.items || [];
r = c.items;
return true;
}
});
return r;
}, tree).push(o);
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');
Update: Version for independent order of items.
var obj = [
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 32, name: "fruits_banana" },
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 11, name: "fruits" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" },
{ id: 999, name: "music_pop_disco_euro"}
],
tree = [];
obj.forEach(function (item) {
var path = item.name.split('_'),
o = tree;
path.forEach(function (a, i) {
var oo = { name: a, items: [] },
last = path.length - 1 === i,
found = o.some(function (b) {
if (b.name === a) {
if (last) {
b.id = item.id;
return true;
}
b.items = b.items || [];
o = b.items;
return true;
}
});
if (!found) {
if (last) {
o.push({ id: item.id, name: a });
} else {
o.push(oo);
o = oo.items;
}
}
});
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');