JavaScript: Add new attribute to object in an array - javascript

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)

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 array objects by one of their fields?

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 }
}, {})

Count duplicates in an array and return new array with a key value derived from another array in Javascript

Following on from my previous question, I'd like to change and extend the capability of what was suggested.
Here's the data I've got:
const things = [
{
id: 1,
title: 'Something',
categoryId: 1
},
{
id: 2,
title: 'Another thing',
categoryId: 1
},
{
id: 3,
title: 'Yet another thing',
categoryId: 2
},
{
id: 4,
title: 'One more thing',
categoryId: 4
},
{
id: 5,
title: 'Last thing',
categoryId: 4
}
]
const categories = [
{
id: 1,
title: 'Category 1'
},
{
id: 2,
title: 'Category 2'
},
{
id: 4,
title: 'Category 3'
}
]
Previously I've been shown how to do something along these lines:
const duplicatesCountWithTitle = (things, categories) => {
const thingsReduced = things.reduce((hash, { categoryId }) => {
hash[categoryId] = (hash[categoryId] || 0) + 1
return hash
}, {})
}
As you'd be able to tell, the problem with this is that it actually returns a new object, and not a new array. Also I'd like to join the categoryTitle from the categories array with the results of the duplicated count from the things array, based on the categoryId matching the id in categories.
// currently the above returns an object in the structure of:
// {
// 1: 2,
// 2: 1,
// 4: 2
// }
// what I'm after is an array like this:
// [
// { 'Category 1': 2 },
// { 'Category 2': 1 },
// { 'Category 3': 2 }
// ]
Thanks in advance, again.
Something like this?
const newArr = categories.map(category => {
const count = things.filter(thing => thing.categoryId === category.id).length;
return { [category.title]: count }
});
console.log(newArr);
https://jsfiddle.net/f3x6m12j/
You could take a Map for the relation of id and title.
const
duplicatesCountWithTitle = (things, categories) => things.reduce((hash, { categoryId }) => {
hash[categories.get(categoryId)] = (hash[categories.get(categoryId)] || 0) + 1
return hash;
}, {}),
things = [{ id: 1, title: 'Something', categoryId: 1 }, { id: 2, title: 'Another thing', categoryId: 1 }, { id: 3, title: 'Yet another thing', categoryId: 2 }, { id: 4, title: 'One more thing', categoryId: 4 }, { id: 5, title: 'Last thing', categoryId: 4 }],
categories = [{ id: 1, title: 'Category 1' }, { id: 2, title: 'Category 2' }, { id: 4, title: 'Category 3' }],
result = duplicatesCountWithTitle(
things,
categories.reduce((m, { id, title }) => m.set(id, title), new Map)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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