Transform Object attribute to array of object - javascript

I want to merge Array of ObjectA containing ObjectB attribute by ObjectA attribute.
For example :
let myArray = [
{ name: 'Jeu', series: { name: 'testA', value: '89' } },
{ name: 'Dim', series: { name: 'testB', value: '490' } },
{ name: 'Dim', series: { name: 'testC', value: '978' } }
]
And I would like to transform it to
[
{ name: 'Jeu', series: { name: 'testA', value: '89' } },
{ name: 'Dim', series: [{ name: 'testB', value: '490' },{ name: 'testC', value: '978' } ] }
]
Am I able to do that with a simple reduce/map loop ?

You can first use reduce (with some spread syntax) to build an object that maps unique names and objects in the format you want to have, grouping series by name. Then, you can simply get the values from this object.
const myArray = [
{ name: 'Jeu', series: { name: 'testA', value: '89' } },
{ name: 'Dim', series: { name: 'testB', value: '490' } },
{ name: 'Dim', series: { name: 'testC', value: '978' } }
];
const map = myArray.reduce(
(acc, curr) => ({
...acc,
[curr.name]: {
name: curr.name,
series: acc[curr.name]
? [...acc[curr.name].series, curr.series]
: [curr.series]
}
}),
{}
);
const output = Object.values(map);
console.log(output);

Related

Extract array from javascript object

I have below javascript object - (named Division)
I want to extract only SubDivs from the array
I tried : -
const division = [{
Name: "DivName1",
Value: "DivValue1",
SubDivision: {
Name: "SubDiv1",
Value: "SubDiv1"
}
},
{
Name: "DivName2",
Value: "DivValue2",
SubDivision: [{
Name: "SubDiv2",
Value: "SubDiv2"
},
{
Name: "SubDiv3",
Value: "SubDiv3"
}
]
}
]
var subDivs = division.map(x => x.SubDivision);
console.log(subDivs)
But this is not giving me array in format -
[{
Name:"SubDiv1",
Value:"SubDiv1"
},
{
Name:"SubDiv2",
Value:"SubDiv2"
},
{
Name:"SubDiv3",
Value:"SubDiv3"
}]
You can use flatMap for that
const division = [{
Name: "DivName1",
Value: "DivValue1",
SubDivision: [{
Name: "SubDiv1",
Value: "SubDiv1"
}]
},
{
Name: "DivName2",
Value: "DivValue2",
SubDivision: [{
Name: "SubDiv2",
Value: "SubDiv2"
},
{
Name: "SubDiv3",
Value: "SubDiv3"
}
]
}
]
const subdivision = division.flatMap(d => d.SubDivision)
console.log(subdivision)
Given your example, all you need to do is call flat on the mapped array:
var subDivs= division.map(x=>x.SubDivision).flat();
Working example:
const division = [{
Name: "DivName1",
Value: "DivValue1",
SubDivision: [{
Name: "SubDiv1",
Value: "SubDiv1"
}
]},
{
Name: "DivName2",
Value: "DivValue2",
SubDivision: [{
Name: "SubDiv2",
Value: "SubDiv2"
},
{
Name: "SubDiv3",
Value: "SubDiv3"
}
]
}
]
var subDivs= division.map(x=>x.SubDivision).flat();
console.log(subDivs)

combine array of object if atleast one property is common

//this one is actual array
const data = [
{
name: 'shanu',
label: 'ak',
value: 1,
},
{
name: 'shanu',
label: 'pk',
value: 2,
},
{
name: 'bhanu',
label: 'tk',
value: 3,
},
];
>
//and this is the array that I want
let outPut =
[
{
name:'shanu',
label:['ak','pk'],
value:[1,2]
},
{
name:'bhanu',
label:['tk'],
value:[3]
}
]
You can use Array.prototype.reduce() like this:
const data = [
{
name: 'shanu',
label: 'ak',
value: 1,
},
{
name: 'shanu',
label: 'pk',
value: 2,
},
{
name: 'bhanu',
label: 'tk',
value: 3,
},
];
const output = data.reduce((prev, curr) => {
const tmp = prev.find((e) => e.name === curr.name)
if (tmp) {
tmp.label.push(curr.label)
tmp.value.push(curr.value)
} else {
prev.push({
name: curr.name,
label: [curr.label],
value: [curr.value],
})
}
return prev
}, [])
console.log(output)

How to split an array of elements into groups by the value of a certain property?

I have an array:
[{
name: "A1"
series: "A series"
},
{
name: "A2"
series: "A series"
},
{
name: "B1"
series: "B series"
},
{
name: "C1"
// series is not defined
}]
I want to split this array into multiple arrays grouped by their values of the series property. I want to arrive at something like the structure below. But as long as the result contains arrays grouped by their values of the series property, the exact format does not matter.
[
{
series: "A series",
items: [{
name: "A1"
},
{
name: "A2"
}]
},
{
series: "B series",
items: [{
name: "B1"
}]
}
{
items: [{
name: "C1"
}]
}
]
Here is my best attempt:
private groupProductsBySeries = (devices: IItem[]): IProductGroup[] => {
const seriesSymbols: any = new Map();
const itemsBySeries: any = [];
const series: any = [];
devices.forEach((device) => {
let symbol = seriesSymbols.get(device.properties.series);
if (!symbol) {
symbol = Symbol();
seriesSymbols.set(device.properties.series, symbol);
}
Array.isArray(itemsBySeries[symbol])
? itemsBySeries[symbol].push(device)
: (itemsBySeries[symbol] = [device]);
});
seriesSymbols.forEach((value: any, key: any) => {
series.push({
name: key,
items: itemsBySeries[value],
});
});
return series;
};
I'm thinking that maybe it is needlessly complex. And when I try to add types, I get complaints from the TypeScript compiler.
Question:
Is there a better way to solve this problem?
If not, how would the code above look with types successfully added?
If not
Edited after adigas comment
function groupProductsBySeries(devices) {
const series = Array.from(new Set(devices.map(item => item.series)));
return series.map(item => {
const series = item ? { series: item } : null;
return {
...series,
items: devices.filter(device => device.series === item)
};
});
}
I'd iterate over the name and series properties of each object, with the series defaulting to the empty string if empty, putting values onto an object whose properties are the series being iterated over, and that series' associated object (with the nested items array). If a series doesn't exist on it yet, create it if needed (and assign a series property if necessary), otherwise just push the { name } to it.
At the end, take the object's values to get your desired output:
const arr = [
{
name: 'A1',
series: 'A series',
},
{
name: 'A2',
series: 'A series',
},
{
name: 'B1',
series: 'B series',
},
{
name: 'C1',
// series is not defined
},
];
type outputItem = {
series?: string,
items: Array<{ name: string }>,
};
const groupedObj: { [series: string]: outputItem } = {};
for (const { name, series='' } of arr) {
if (!groupedObj[series]) {
groupedObj[series] = { items: [{ name }] };
if (series) {
groupedObj[series].series = series;
}
} else {
groupedObj[series].items.push({ name });
}
}
const output = Object.values(groupedObj);
console.log(output);
Translated into Javascript:
"use strict";
const arr = [
{
name: 'A1',
series: 'A series',
},
{
name: 'A2',
series: 'A series',
},
{
name: 'B1',
series: 'B series',
},
{
name: 'C1',
},
];
const groupedObj = {};
for (const { name, series = '' } of arr) {
if (!groupedObj[series]) {
groupedObj[series] = { items: [{ name }] };
if (series) {
groupedObj[series].series = series;
}
}
else {
groupedObj[series].items.push({ name });
}
}
const output = Object.values(groupedObj);
console.log(output);
This object structure is pretty peculiar, though. It would probably be a lot easier to work with a single object, whose keys are the series, and whose values are arrays of name values:
const arr = [
{
name: 'A1',
series: 'A series',
},
{
name: 'A2',
series: 'A series',
},
{
name: 'B1',
series: 'B series',
},
{
name: 'C1',
// series is not defined
},
];
const output: { [series: string]: string[] } = {};
for (const { name, series='' } of arr) {
if (!output[series]) {
output[series] = [name];
} else {
output[series].push(name);
}
}
console.log(output);
Translated to Javascript:
"use strict";
const arr = [
{
name: 'A1',
series: 'A series',
},
{
name: 'A2',
series: 'A series',
},
{
name: 'B1',
series: 'B series',
},
{
name: 'C1',
},
];
const output = {};
for (const { name, series = '' } of arr) {
if (!output[series]) {
output[series] = [name];
}
else {
output[series].push(name);
}
}
console.log(output);
You could write a generic function that accepts any field and group by them. Something like below could help.
data = [{
name: "A1",
series: "A series"
},
{
name: "A2",
series: "A series"
},
{
name: "B1",
series: "B series"
},
{
name: "C1",
// series is not defined
}]
data1 = [{
name: "A",
series: "A series"
},
{
name: "A",
series: "A series"
},
{
name: "B",
series: "B series"
},
{
name: "C",
// series is not defined
}]
function groupByField(data, field){
const groupedByObject = data.reduce((acc, val) => {
const rest = Object.keys(val).reduce((newObj, key) => {
if(key !== field){
newObj[key] = val[key]
}
return newObj;
}, {});
if (acc[val[field]]) {
acc[val[field]].push(rest);
} else {;
acc[val[field]] = [rest];
}
return acc;
}, {})
//Return the reduced object from above if want in Object format. If wanted array, return the below statement
return Object.keys(groupedByObject).filter(a => a!== "undefined").map(key => ({[field]: key, items: groupedByObject[key]}))
}
a = groupByField(data,"series");
console.log("Grouped by series")
console.log(a);
b = groupByField(data1, "name");
console.log("Grouped by name")
console.log(b);

Javascript filtering nested arrays

I'm trying to filter a on a nested array inside an array of objects in an Angular app. Here's a snippet of the component code -
var teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
What I'm trying to achieve is if I search for m5 for example my result should be -
var teams = [
{ name: 'Team1', members: [] },
{ name: 'Team2', members: [{ name: 'm5' }] },
{ name: 'Team3', members: [] }
];
So I've got teams and filteredTeams properties and in my search function I'm doing -
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = this.teams.map(t => {
t.members = t.members.filter(d => d.name.toLowerCase().includes(value));
return t;
})
}
Now this does work to some extent however because I'm replacing the members it's destroying the array on each call (if that makes sense). I understand why this is happening but my question is what would be the best way to achieve this filter?
you were very close, the only thing that you did wrong was mutating the source objects in teams
basically you can use spread operator to generate a new entry and then return a whole new array with new values.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const value = 'm5';
const result = teams.map(t => {
const members = t.members.filter(d => d.name.toLowerCase().includes(value));
return { ...t, members };
})
console.log(result)
Check this. Instead of hard coded m5 pass your value.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const filteredTeams = teams.map(team => ({ name: team.name, members: team.members.filter(member => member.name.includes('m5')) }));
console.log(filteredTeams);
You are mutating the original objects, but you could assing new properties to the result object for mapping instead.
var teams = [{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] }, { name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] }, { name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }],
result = teams.map(o => Object.assign(
{},
o,
{ members: o.members.filter(({ name }) => name === 'm5') }
));
console.log(result);
console.log(teams);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try to seperate your filter function first:
const filterTeamMembers = (teams, filterArr) => {
const useFilter = filterArr.map(x => x.toLowerCase());
return teams.map(team => ({
...team,
members: team.members.filter(member => useFilter.includes(member.name))
}))
};
// =========== And then:
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = filterTeamMembers(this.teams, [value]);
}

Concat array from Object from Array

I'm currently trying to retrieve a list of metadata stored as an array, inside an object, inside an array. Here's a better explanatory example:
[
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
]
Now, my objective is to retrieve a list of metadata (by their name) without redundancy. I think that .map() is the key to success but I can't find how to do it in a short way, actually my code is composed 2 for and 3 if, and I feel dirty to do that.
The expected input is: ['Author', 'Creator', 'Created', 'Date']
I'm developping in Typescript, if that can help for some function.
You can use reduce() and then map() to return array of names.
var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
var result = [...new Set(data.reduce(function(r, o) {
if (o.metadata) r = r.concat(o.metadata.map(e => e.name))
return r
}, []))];
console.log(result)
You could use Set for unique names.
var data = [{ name: 'test', metadata: [{ name: 'Author', value: 'foo' }, { name: 'Creator', value: 'foo' }] }, { name: 'otherTest', metadata: [{ name: 'Created', value: 'foo' }, { name: 'Date', value: 'foo' }] }, { name: 'finalTest' }],
names = new Set;
data.forEach(a => (a.metadata || []).forEach(m => names.add(m.name)));
console.log([...names]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
data
.filter(function(obj){return obj.metadata != undefined})
.map(function(obj){return obj.metadata})
.reduce(function(a,b){return a.concat(b)},[])
.map(function(obj){return obj.name})
A hand to hand Array.prototype.reduce() and Array.prototype.map() should do it as follows;
var arr = [
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
];
result = arr.reduce((p,c) => c.metadata ? p.concat(c.metadata.map(e => e.name))
: p, []);
console.log(result);

Categories

Resources