How to group array objects by one of their fields? - javascript

I'm trying to restructure an array by filtering it from another list of array.
Array Containing Filters:
const filteringTags: [
'Kitchen',
'Accessories',
...
]
Array I Would Like to Manipulate:
[
{
category: 'free',
date: '2020-04-04',
title: 'Some Title',
tags: [
'Kitchen',
'Accessories'
]
},
{
category: 'premium',
date: '2020-04-05',
title: 'Another Title',
tags: [
'Kitchen'
]
},
...
]
Expected Result:
[
'Kitchen': [
{
category: 'free',
date: '2020-04-04',
title: 'Some Title',
tags: [
'Kitchen',
'Accessories'
]
},
{
category: 'premium',
date: '2020-04-05',
title: 'Another Title',
tags: [
'Kitchen'
]
}
],
'Accessories': [
{
category: 'free',
date: '2020-04-04',
title: 'Some Title',
tags: [
'Kitchen',
'Accessories'
]
}
]
]
I'm trying to avoid using libraries so if you have any suggestions I would appreciate if you use vanilla JS.

Considering the required output as array of objects,
const data = [
{
category: 'free',
date: '2020-04-04',
title: 'Some Title',
tags: ['Kitchen', 'Accessories'],
},
{
category: 'premium',
date: '2020-04-05',
title: 'Another Title',
tags: ['Kitchen'],
},
];
const filteringTags = ['Kitchen', 'Accessories'];
const getGroupedData = (data, filters) => {
const result = {};
filters.forEach(filter => {
result[filter] = [
...JSON.parse(JSON.stringify((result[filter] || ''))),
...JSON.parse(JSON.stringify(data.filter(d => d.tags.includes(filter)))),
];
});
return result;
};
let finalResult = getGroupedData(data, filteringTags);
finalResult = Object.keys(finalResult).map(key => ({
[key]: finalResult[key],
}));
console.log(finalResult);
Considering the output as object
const data = [
{
category: 'free',
date: '2020-04-04',
title: 'Some Title',
tags: ['Kitchen', 'Accessories'],
},
{
category: 'premium',
date: '2020-04-05',
title: 'Another Title',
tags: ['Kitchen'],
},
];
const filteringTags = ['Kitchen', 'Accessories'];
const getGroupedData = (data, filters) => {
const result = {};
filters.forEach(filter => {
result[filter] = [
...JSON.parse(JSON.stringify((result[filter] || ''))),
...JSON.parse(JSON.stringify(data.filter(d => d.tags.includes(filter)))),
];
});
return result;
};
let finalResult = getGroupedData(data, filteringTags);
console.log(finalResult);
Hope this helps.

Here's a full working example which produces the desired result:
const filteringTags = [
'Kitchen',
'Accessories',
];
const data = [
{
category: 'free',
date: '2020-04-04',
title: 'Some Title',
tags: [
'Kitchen',
'Accessories'
]
},
{
category: 'premium',
date: '2020-04-05',
title: 'Another Title',
tags: [
'Kitchen'
]
},
];
function byTag(tags, data) {
let tagMap = {};
for (let tag of tags) {
tagMap[tag] = [];
for (let datum of data) {
if (datum.tags.includes(tag)) {
tagMap[tag].push(datum);
}
}
}
return tagMap;
}
let result = byTag(filteringTags, data);
console.log(JSON.stringify(result, null, 2));

You can do something like this:
var arr = [
{
category: 'free',
date: '2020-04-04',
title: 'Some Title',
tags: [
'Kitchen',
'Accessories'
]
},
{
category: 'premium',
date: '2020-04-05',
title: 'Another Title',
tags: [
'Kitchen'
]
},
];
var res = arr.reduce((acc, elem)=>{
elem.tags.forEach(k=>{
acc[k] = acc[k] ? [...acc[k], JSON.parse(JSON.stringify(elem))] : [JSON.parse(JSON.stringify(elem))];
})
return acc
},{});
console.log(res)

You can do this with :
reduce()
filter()
var filteringTags= [ 'Kitchen', 'Accessories'];
var arr=[ { category: 'free', date: '2020-04-04', title: 'Some Title', tags: [ 'Kitchen', 'Accessories' ] }, { category: 'premium', date: '2020-04-05', title: 'Another Title', tags: [ 'Kitchen' ] }];
var result = filteringTags.reduce((acc, e)=>{
acc[e] = arr.filter(k=>k.tags.includes(e));
return acc;
},{});
console.log(result);

const filteringTags = [
'Kitchen',
'Accessories',
]
const arr = [
{
category: 'free',
date: '2020-04-04',
title: 'Some Title',
tags: [
'Kitchen',
'Accessories'
]
},
{
category: 'premium',
date: '2020-04-05',
title: 'Another Title',
tags: [
'Kitchen'
]
}
]
const resultArr = filteringTags.map((it, index) => {
return { [it]: arr }
})
If you need object, so use this function
const resultObj = filteringTags.reduce((acc, it) => {
return { ...acc, [it]: arr }
}, {})

Related

Filter does not return the correct result

I have this array and I created this function that return me the filtered array:
const result = [{
key: 'A',
title: 'titleA',
data: [{
name: 'miael',
id: 'id4',
},
{
name: 'top',
id: 'id2',
}
]
},
{
key: 'B',
title: 'titleB',
data: [{
name: 'mich1',
id: 'id12',
},
{
name: 'tomato',
id: 'id123',
}
]
},
]
const doSearch = (data) => result.filter(entry =>
entry.data.some(item =>
item.name
.toString()
.toLowerCase()
.includes(data.toString().toLowerCase().trim()),
),
);
console.log(doSearch('mich'));
This works, but it also returns results that do not contain the searched word 'mic'
if I search for mic, I expect this result:
[{
key: 'B',
title: 'titleB',
data: [{
name: 'mich1',
id: 'id12',
},
]
}],
what am I doing wrong?
A couple of changes should make this work the way you wish.
Turning doSearch into a function.
Adding a searchFor parameter to the doSearch() function and passing to the .includes() call.
Using Array.reduce() to create the output array. Items are only added if they include the searchFor value.
const input = [{ key: 'A', title: 'titleA', data: [{ name: 'miael', id: 'id4', }, { name: 'top', id: 'id2', } ] }, { key: 'B', title: 'titleB', data: [{ name: 'mich1', id: 'id12', }, { name: 'tomato', id: 'id123', } ] }, ]
const doSearch = (searchFor, arr) => arr.reduce((acc, { key, title, data }) => {
const filteredData = data.filter(({ name }) => {
return name.toLowerCase().includes(searchFor.toLowerCase())
});
if (filteredData.length > 0) {
acc.push({ key, title, data: filteredData });
}
return acc;
}, []);
console.log(doSearch('mic', input ));
You can keep your current logic and add a map with the same filter for entry.data:
const result = [{
key: 'A',
title: 'titleA',
data: [{
name: 'miael',
id: 'id4',
},
{
name: 'top',
id: 'id2',
}
]
},
{
key: 'B',
title: 'titleB',
data: [{
name: 'mich1',
id: 'id12',
},
{
name: 'tomato',
id: 'id123',
}
]
},
]
function nameFilter(item, data) {
return item.name
.toString()
.toLowerCase()
.includes(data.toString().toLowerCase().trim())
}
const doSearch = (data) => result.filter(entry =>
entry.data.some(item =>
nameFilter(item, data)
),
).map(entry => ({
...entry,
data: entry.data.filter(item => nameFilter(item, data))
}));
console.log(doSearch('mich'));

JavaScript: Add new attribute to object in an array

I am trying to add an item into an existing object in an array (index each array):
const dataInput = [
{ title: 'first', description: 'test 1' },
{ title: 'second', description: 'test 1' },
]
This is what I've tried:
dataInput.map((data, index) => {
availableItems.push({'idx':index})
})
This pushes a new object instead of adding the element to the existing first and second.
[
{ title: 'first', description: 'test 1' },
{ title: 'second', description: 'test 1' },
{idx:0},
{idx:1}
]
How could I achieve that? (below is what I need)
[
{ title: 'first', description: 'test 1', idx: 0 },
{ title: 'second', description: 'test 1', idx:1 },
]
You need to add a new attribute at each iteration:
const dataInput = [
{ title: 'first', description: 'test 1' },
{ title: 'second', description: 'test 1' },
];
const res = dataInput.map( (data, index) => ({...data, idx:index}) );
console.log(res);
Another option:
dataInput.forEach((element, index) => (element["idx"] = index));
Another option:
const dataInput= [
{ title: 'first', description: 'test 1' },
{ title: 'second', description: 'test 1' },
]
const result = dataInput.reduce((acc, cur, index) => {
acc.push({...cur, idx: index})
return acc
},[])
console.log(result)

How to group object inside array

Here is what I have
[
{
typeProgramId: {
name: 'type1',
},
title: 'test1',
},
{
typeProgramId: {
name: 'type1',
},
subTypeProgramId: [{
name: 'sub1',
}],
title: 'test2',
},
{
typeProgramId: {
name: 'type2',
},
title: 'test3',
},
{
typeProgramId: {
name: 'type2',
},
subTypeProgramId: {
name: 'sub2',
},
title: 'test4',
}
]
First I want to group typeProgramId if the title have the same typeProgramId I want to push title into array by each typeProgramId but If the data have typeProgramId and subTypeProgram Id I want to group subtypeProgramId in typeProgramId too. if not empty subtypeProgramId I want to push it in array title inside subtypeProgram Id. I try to use lodash groupBy and many way but it still did not work.
Here is what I want
{
typeProgramId: [{
name: 'type1',
title: [
'test1',
],
subTypeProgramId: {
name: sub1,
title: [
'test2'
]
}
}, {
name: 'type2',
title: [
'test3',
],
subTypeProgramId: [{
name: sub1,
title: [
'test4'
]
}
}]
}
what I do now
let result = _.groupBy(getProgram, function(data) {
return data.typeProgramId
})
result = _.map(result, function(group, data) {
// I think in here I must groupBy subTypeProgramId again
// the data return all string not object after group
return {
typeProgramId: data,
titile: group,
}
})
Please check the below code. I have used reduce function of Array. It produces the expected result.
function updateMem(mem, prgIndex, val){
if(prgIndex < 0) {
mem.typeProgramId.push({});
prgIndex = mem.typeProgramId.length - 1;
}
mem.typeProgramId[prgIndex].name = val.typeProgramId.name;
if(val.subTypeProgramId){
mem.typeProgramId[prgIndex].subTypeProgramId = Object.assign({}, mem.typeProgramId[prgIndex].subTypeProgramId || {}, {"name" : val.subTypeProgramId.name, "title": []});
mem.typeProgramId[prgIndex].subTypeProgramId.title.push(val.title);
} else {
mem.typeProgramId[prgIndex].title = (mem.typeProgramId[prgIndex].title ? mem.typeProgramId[prgIndex].title : []);
mem.typeProgramId[prgIndex].title.push(val.title);
}
};
arr.reduce((mem, val) => {
var prgIndex = mem.typeProgramId.findIndex((p) => p.name === val.typeProgramId.name);
updateMem(mem, prgIndex, val);
return mem;
}, {typeProgramId: []});

format data using .map and .filter

i got a following type of result from the data base when i fetch database. i have tried many thing and serch google but could't found anything. please help me with this. thank you.
{ metaData:
[ { name: 'ID' },
{ name: 'NAME' },
{ name: 'LED_ID' },
{ name: 'LED_ORG_ID' },
{ name: 'COMPANY_ADD' },
{ name: 'STATE_CODE' },
{ name: 'CIN_NO' } ],
rows:
[ [ 1,
'company name',
2481,
'161',
'address ',
'27',
'number' ],
[ 2,
'company name2',
2581,
'164',
'address 2',
'27',
'number2' ]
}
}
I am trying to achieve below formatted data
{
data:[
{
ID:1,
NAME:'company name',
LED_ID:2481,
LED_ORG_ID: '161',
COMPANY_ADD:'address',
STATE_CODE:'27',
CIN_NO:'number'
},
{
ID:2,
NAME:'company name 2',
LED_ID:2581,
LED_ORG_ID: '164',
COMPANY_ADD:'address 2',
STATE_CODE:'27',
CIN_NO:'number 2'
}
]
}
You could get the keys first and then map the object from the entries.
var data = { metaData: [{ name: 'ID' }, { name: 'NAME' }, { name: 'LED_ID' }, { name: 'LED_ORG_ID' }, { name: 'COMPANY_ADD' }, { name: 'STATE_CODE' }, { name: 'CIN_NO' }], rows: [[1, 'company name', 2481, '161', 'address ', '27', 'number'], [2, 'company name2', 2581, '164', 'address 2', '27', 'number2']] },
keys = data.metaData.map(({ name }) => name),
result = { data: data.rows.map(a => Object.fromEntries(keys.map((k, i) => [k, a[i]]))) };
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Use map in conjunction with flatMap and reduce:
const metaData = [{name:'ID'},{name:'NAME'},{name:'LED_ID'},{name:'LED_ORG_ID'},{name:'COMPANY_ADD'},{name:'STATE_CODE'},{name:'CIN_NO'}];
const rows = [[1,'company name',2481,'161','address ','27','number'],[2,'company name2',2581,'164','address 2','27','number2']];
const res = rows.flatMap(e => e.map((f, i) => ({ [metaData[i].name]: f })).reduce((a, c) => ({ ...a, ...c }), {}));
console.log(res);
More performant solution thanks to Mark Meyer:
const res = rows.map(e => e.reduce((a, c, i) => ({ ...a, ...{ [metaData[i].name]: c }}), {});
You can use array.map() and Object.fromEntires():
let data = { metaData:
[ { name: 'ID' },
{ name: 'NAME' },
{ name: 'LED_ID' },
{ name: 'LED_ORG_ID' },
{ name: 'COMPANY_ADD' },
{ name: 'STATE_CODE' },
{ name: 'CIN_NO' } ],
rows:
[ [ 1,
'company name',
2481,
'161',
'address ',
'27',
'number' ],
[ 2,
'company name2',
2581,
'164',
'address 2',
'27',
'number2' ]
]
}
let result = data.rows.map(
entry => Object.fromEntries(
entry.map((x, i) => [data.metaData[i].name, x])
)
)
console.log(result)
EDIT: The outer map transforms rows so there will be two objects returned. The inner one transforms all the values into format like ["ID", 1]. That array of arrays is passed as an argument into Object.fromEntries which creates a new object based on those pairs.
let data = {
metaData: [{
name: 'ID'
},
{
name: 'NAME'
},
{
name: 'LED_ID'
},
{
name: 'LED_ORG_ID'
},
{
name: 'COMPANY_ADD'
},
{
name: 'STATE_CODE'
},
{
name: 'CIN_NO'
}
],
rows: [
[1,
'company name',
2481,
'161',
'address ',
'27',
'number'
],
[2,
'company name2',
2581,
'164',
'address 2',
'27',
'number2'
]
]
}
let transform = (meta, item) => {
return meta.map((a, i) => ({
[a.name]: item[i]
}))
}
let result = data.rows.map(i => transform(data.metaData, i))
console.log(result.map(i => Object.assign({}, ...i)))
It can be better...

Map list of objects with sub array of objects

I have this data structure that i want to map in an es6 one-liner fashion:
const vehicles = [
{
id: 'vehicle1',
items: [
{
id: 'contract1'
name: 'Contract 1',
},
],
},
{
id: 'vehicle1',
items: [
{
id: 'contract2'
name: 'Contract 2',
},
],
},
{
id: 'vehicle2',
items: [
{
id: 'contract3'
name: 'Contract 3',
},
],
},
{
id: 'vehicle2',
items: [
{
id: 'contract4'
name: 'Contract 4',
},
],
},
]
I would like to collect this in a list like this:
const result = [
{
id: 'vehicle1',
items: [
{
id: 'contract1'
name: 'Contract 1',
},
{
id: 'contract2'
name: 'Contract 2',
},
],
},
{
id: 'vehicle2',
items: [
{
id: 'contract3'
name: 'Contract 3',
},
{
id: 'contract4'
name: 'Contract 4',
},
],
},
]
So the vehicles in list is unique and items is in one list.
I tried this but it only collects vehicles in list:
const res = vehicles.reduce((acc, vehicle) => acc.set(vehicle.id, vehicle), new Map())
How can I do this the 'ES6 way'?
Map would be not a good choice for this type of result, Map used mostly when you have to modify and get same structure. You can use reduce for this.
var data = [{
id: 'vehicle1',
items: [{
id: 'contract1',
name: 'Contract 1'
}]
},
{
id: 'vehicle1',
items: [{
id: 'contract2',
name: 'Contract 2'
}]
},
{
id: 'vehicle2',
items: [{
id: 'contract3',
name: 'Contract 3'
}]
},
{
id: 'vehicle2',
items: [{
id: 'contract4',
name: 'Contract 4'
}]
}
];
var result = {};
data.forEach(val => {
if (result[val.id])
result[val.id].items = result[val.id].items.concat(val.items);
else
result[val.id] = val
});
result = Object.values(result);
console.log(result);
You were on the right path. Here it is:
const res = vehicles.reduce((m,v)=>m.set(v.id, [...v.items, ...(m.get(v.id)||[])]), new Map)
This use array destructuring to concat items.
You can use Array.prototype.reduce to aggregate the input by id and Object.keys to get the output in the desired format
const vehicles=[{id:'vehicle1',items:[{id:'contract1',name:'Contract 1'}]},{id:'vehicle1',items:[{id:'contract2',name:'Contract 2'}]},{id:'vehicle2',items:[{id:'contract3',name:'Contract 3'}]},{id:'vehicle2',items:[{id:'contract4',name:'Contract 4'}]}];
const grouped = vehicles.reduce((all, {id, items}) => {
if (!all.hasOwnProperty(id)) all[id] = { id, items: [] };
all[id].items.push(...items);
return all;
}, {});
const result = Object.keys(grouped).map(k => grouped[k]);
console.log(result);
Not a one-liner but it returns desired result and uses ES6 Map.
const data = [{"id":"vehicle1","items":[{"id":"contract1","name":"Contract 1"}]},{"id":"vehicle1","items":[{"id":"contract2","name":"Contract 2"}]},{"id":"vehicle2","items":[{"id":"contract3","name":"Contract 3"}]},{"id":"vehicle2","items":[{"id":"contract4","name":"Contract 4"}]}]
const res = data.reduce((acc, {id, items}) => {
if(!acc.get(id)) acc.set(id, {id, items});
else acc.get(id).items.push(...items);
return acc;
}, new Map())
console.log([...res.values()])
Well, its not a one liner but it can be...if you delete all the line breaks :D
const convert = () => {
const vMap = vehicles.reduce((acc, vehicle) => {
if (acc[vehicle.id]) {
acc[vehicle.id].items.push(...vehicle.items);
} else {
acc[vehicle.id] = vehicle;
}
return acc;
}, {});
return Object.keys(vMap).map(k => vMap[k]);
};
convert();
Nearly, you could get the grouped items in a map and map the map with the wanted id and itmes property.
const
vehicles = [{ id: 'vehicle1', items: [{ id: 'contract1', name: 'Contract 1', }] }, { id: 'vehicle1', items: [{ id: 'contract2', name: 'Contract 2', }] }, { id: 'vehicle2', items: [{ id: 'contract3', name: 'Contract 3', }] }, { id: 'vehicle2', items: [{ id: 'contract4', name: 'Contract 4' }] }],
result = Array.from(
vehicles.reduce((acc, { id, items }) =>
acc.set(id, (acc.get(id) || []).concat(items)), new Map()),
([id, items]) => ({ id, items })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories