I have data array object like this:
const data = [
{Name: "dian", Job: "programmer"},
{Name: "dian", Job: "gamer"},
{Name: "candra", Job: "programmer"},
]
My goal is to create new data where a have same value join b.
Example output:
const new_data = [
{Name: "dian", Jobs: [{Job: "programmer"}, {Job: "gamer"}]},
{Name: "candra", Jobs: [{Job: "programmer"}]},
]
I think you can use Array.prototype.reduce() to achive your goal. From the documentation:
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
One possible solution:
const data = [
{Name:'dian', Job:'programer'},
{Name:'dian', Job:'gamers'},
{Name:'candra', Job:'programer'}
];
const result = data.reduce((a, current) => {
const found = a.find(f => f.Name === current.Name);
if (found) {
found.Jobs.push({Job: current.Job});
} else {
a.push({
Name: current.Name,
Jobs: [
{ Job: current.Job }
]
});
}
return a;
}, []);
console.log(result);
I hope that helps!
use reduce.
const data = [
{ Name: "dian", Job: "programer" },
{ Name: "dian", Job: "gamers" },
{ Name: "candra", Job: "programer" }
];
const output = Object.values(data.reduce((a, { Name, Job }, i) => {
if (!a[Name]) {
a[Name] = { Name, Jobs: [] };
}
a[Name].Jobs.push({ Job });
return a;
}, {}));
console.log(output);
Here's one approach:
const combineJobs = (data) =>
Object .values (data .reduce (
(a, {Name, Job}, _, __, curr = a [Name] || {Name, jobs: []}) =>
({... a, [Name]: ({... curr, jobs: [... curr .jobs, {Job}]})}),
{}
))
const data = [{Name: "dian", Job: "programmer"}, {Name: "dian", Job: "gamer"}, {Name: "candra", Job: "programmer"}]
console .log (combineJobs (data))
We simply fold our objects into a structure that looks like
{
dian: {Name: 'dian', jobs: [Job:'programer'}, {Job: 'gamer'}]},
candra: {Name: 'candra', jobs: [Job:'programer'}]},
}
then use Object .values to turn it into an appropriate array.
One advantage of this technique is that if your data actually has additional fields not displayed in the question (imagine you have age, and avatar properties as well, for instance), you can extend it easily using a rest parameter:
const combineJobs = (data) =>
Object .values (data .reduce (
(a, {Name, Job, ...rest}, _, __, curr = a [Name] || {Name, jobs: [], ...rest}) =>
// ^^^^^^^ ^^^^^^^
({... a, [Name]: ({... curr, jobs: [... curr .jobs, {Job}]})}),
{}
))
and all those additional parameters would be included.
function modifyArray(data) {
function getNewObject(data) {
const newObject = {
Name: data.Name
};
Object.keys(data).filter(key => key !== 'Name').forEach(key => {
newObject[key+'s'] = [];
newObject[key+'s'].push({
[key]: data[key]
});
});
return newObject;
}
function appendData(obj, data) {
Object.keys(data).filter(key => key !== 'Name').forEach(key => {
obj[key+'s'].push({
[key]: data[key]
});
});
}
const reqArray = [];
data.forEach(d => {
const objToModify = reqArray.find(a => a.Name === d.Name);
if (!objToModify) {
reqArray.push(getNewObject(d));
} else {
appendData(objToModify, d);
}
});
return reqArray;
}
let data =[
{Name:'dian', Job:'programer' },
{Name:'dian', Job:'gamers' },
{Name:'candra', Job:'programer' }
];
console.log(modifyArray(data));
Related
I'm merging two objects together to create a filter object. However I want to group the merged objects property values where the keys are the same.
So...
[{category: 'furniture'}, {category: 'mirrors'}, {availability: 'in_stock'}]
becomes
[{category: ['furniture', 'mirrors']}, {availability: 'in_stock'}]
any ideas?
With lodash you merge the entire array to a new object by spreading into _.mergeWith(). The customizer should use empty arrays as default values for the current values, and concat the values. Use _.map() to convert back to an array.
const data = [{category: 'furniture'}, {category: 'mirrors'}, {availability: 'in_stock'}];
const result = _.map(
_.mergeWith({}, ...data, (a = [], b = [], key) => a.concat(b)),
(val, key) => ({ [key]: val })
)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Using vanilla JS, reduce the array to a Map using the objects' keys as the keys of the Map, with an empty array as the value, and push the objects' values into the arrays. Use Array.from() to convert the Map to an array.
const data = [{category: 'furniture'}, {category: 'mirrors'}, {availability: 'in_stock'}];
const result = Array.from(
data.reduce((acc, obj) => {
Object.entries(obj)
.forEach(([key, val]) => {
if(!acc.has(key)) acc.set(key, [])
acc.get(key).push(val)
})
return acc
}, new Map()),
([key, val]) => ({ [key]: val })
)
console.log(result)
You can use reduce like this:
const data = [
{ category: 'furniture' },
{ category: 'mirrors' },
{ availability: 'in_stock' }
];
const result = data.reduce(
(a, x) => {
const key = Object.keys(x)[0]; // find the key of the current object
if (!a.tmp[key]) { // if the current key doesn't exist in the lookup object (tmp) yet ...
a.tmp[key] = []; // create an empty array in the lookup object for the current key
a.result.push({ [key]: a.tmp[key] }); // push the current object to the result
}
a.tmp[key].push(x[key]); // push the current value to the array
return a;
},
{ result: [], tmp: {} },
).result;
console.log(result);
I'm sure there are easier ways to achieve this, but that's the best I can come up with right now.
we can also achieve this by using forEach loop :
const input = [{category: 'furniture'}, {category: 'mirrors'}, {availability: 'in_stock'}];
const resultObj = {};
const resultArr = [];
input.forEach((obj) => {
resultObj[Object.keys(obj)[0]] = [];
})
input.forEach((obj) => {
resultObj[Object.keys(obj)[0]].push(obj[Object.keys(obj)[0]]);
resultArr.push(resultObj);
})
console.log([...new Set(resultArr)]);
Another one reduce solution
const arr = [{category: 'furniture', category2: 'furniture2'}, {category: 'mirrors'}, {availability: 'in_stock'}]
const result = Object.values(arr
.flatMap((obj) => Object.entries(obj))
.reduce((acc, [key, value]) => {
acc[key] = acc[key]
? {[key]: [...acc[key][key], value] }
: {[key]: [value] }
return acc;
}, {}));
console.log(result)
.as-console-wrapper{min-height: 100%!important; top: 0}
A generic implementation could achieve a merger of any kind of objects regardless of amount and kind of an(y) object's property names.
Since the result of such an implementation is an object, one needs additional treatment in order to cover the OP's requirement(s).
function mergeAndCollectItemEntries(result, item) {
// return the programmatically aggregated merger/result.
return Object
// get an item's entry array.
.entries(item)
// for each key-value pair ...
.reduce((merger, [key, value]) => {
// ... access and/or create a `key` specific array ...
// ... and push `value` into this array.
(merger[key] ??= []).push(value);
// return the programmatically aggregated merger/result.
return merger;
}, result);
}
const sampleData = [
{ category: 'furniture' },
{ category: 'mirrors' },
{ availability: 'in_stock' },
];
const mergedData = sampleData
.reduce(mergeAndCollectItemEntries, {});
const mergedDataList = Object
.entries(
sampleData
.reduce(mergeAndCollectItemEntries, {})
)
.map(entry => Object.fromEntries([entry]));
//.map(([key, value]) => ({ [key]: value }));
console.log({
sampleData,
mergedData,
mergedDataList,
});
console.log(
Object
.entries([
{ category: 'furniture', foo: 'baz' },
{ category: 'mirrors', bar: 'bizz' },
{ availability: 'in_stock', bar: 'buzz' },
].reduce(
mergeAndCollectItemEntries, {}
)
).map(
([key, value]) => ({ [key]: value })
//entry => Object.fromEntries([entry])
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Another approach here with building an tracking object to merge the values.
Handle the cases of single value keep as string and multiple values as array per the expected output.
const merge = (arr, output = {}) => {
arr.forEach((item) => {
const [[key, val]] = Object.entries(item);
if (key in output) {
output[key] = Array.isArray(output[key])
? output[key].concat(val)
: [output[key]].concat(val);
} else {
output[key] = val;
}
});
return Object.entries(output).map(([key, val]) => ({ [key]: val }));
};
const data = [
{ category: "furniture" },
{ category: "mirrors" },
{ availability: "in_stock" },
];
console.log(merge(data));
Use Case 1
Assuming i have 2dArray Object of
let arr = [{'getName':'Report1'},{'getName':'User'},{'getName':'report 2'},{'getName':'User'},{'getName':'User'}]
let _NotRequiredSheet = ['User','Report 254',...]
Im trying to optimise my script with functional programming which will return me an array of
['report1','report2']
The current Method im using which does not have any error is :
for(let i =0;i < arr.length;i++){
if(arr[i].getName != _NotRequiredSheet[0]){
console.log(arr[i].getName)
}
}
But this will impact if _notrequiredSheet have a big list of what is not required
I tried using this approach which is using filter but since its 2dObject Array, im unsure how should this be implemented.
What i did on my poc is
//Approach 1 : Not Working
let result = arr.filter(function (arr) {
return arr.getName != _NotRequiredSheet.values();
})
//Output should be as 1dArray['report1','report2'] , not (5) [{…}, {…}, {…}, {…}, {…}]
console.log(result)
//Approach 2 : Will output as 2D array with filtered value
// Will require to hardcord the index which is not advisable
let result = arr.filter(function (arr) {
return arr.getName != _NotRequiredSheet[0];
})
console.log(result)
i wanted to check if there is any way i could pass on using for loop with filter function. Result should return as 1D array which is
['Report1','Report2']
Use case 1 is Solved
Use Case 2 : 2D Object Array
Assuming data is declared as
let arr2 = [
{$0:{'Name':'Report1'}},
{$0:{'Name':'Report2'}},
{$0:{'Name':'User'}}
]
Result should show this on console.log (2) [{…}, {…}] , filter function will remove 'User' as its reflected in _NotRequiredSheet.
Using the syntax i wrote
let result = arr2.map(item => item.$0.Name).filter(Name => !_NotRequiredSheet.includes(Name))
This will return as a single array
You could filter your data with looking for unwanted values and map only the wanted property.
const
data = [{ getName: 'Report1' }, { getName: 'User' }, { getName: 'report 2' }, { getName: 'User' }, { getName: 'User' }],
_NotRequiredSheet = ['User', 'Report 254'],
result = data
.filter(({ getName }) => !_NotRequiredSheet.includes(getName))
.map(({ getName }) => getName);
console.log(result);
With a Set
const
data = [{ getName: 'Report1' }, { getName: 'User' }, { getName: 'report 2' }, { getName: 'User' }, { getName: 'User' }],
_NotRequiredSheet = ['User', 'Report 254'],
take = k => o => o[k],
hasNot = s => v => !s.has(v),
comp = f => g => o => f(g(o)),
result = data
.filter(
comp(hasNot(new Set(_NotRequiredSheet)))(take('getName'))
)
.map(({ getName }) => getName);
console.log(result);
I'd recommend using reduce()
so you can return something based on _NotRequiredSheet.includes(cur.getName)
let arr = [{'getName':'Report1'},{'getName':'User'},{'getName':'report 2'},{'getName':'User'},{'getName':'User'}]
let _NotRequiredSheet = ['User','Report 254' ];
let res = arr.reduce((prev, cur) => {
if (_NotRequiredSheet.includes(cur.getName)) {
return prev;
} else {
return [ ...prev, cur.getName ];
}
}, []);
console.log(res);
I have a source array and target array, based on the target array need to update the source array
sourceAry = [{name:'Label1', value: 'label1', children:[{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]},
{name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'}]},
];
targetAry = [{name:'Label1', value: 'label1', children:[{name:'Anu'}]},
{name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'}]},
];
resultAry = [{name:'Label1', value: 'label1', children:[{name:'Ammu'},{name:'Rahual'}]}
]},
];
Code which I try
let resultAry = sourceAry.map((obj) => {
obj.children.map((elem) =>{
targetAry.filter(parent => parent.children.filter((el) => {
el.name !== elem.name}))
})
})
console.log(resultAry, 'NEW', list);
you could start start with some facilities to make it simpler:
const indexBy = (f, data) => data.reduce((acc, x) => Object.assign(acc, { [f(x)]: x }), {})
const remove = (keyFn, dataToRemove, from) => {
const dataToRemoveIndexed = indexBy(keyFn, dataToRemove);
return from.filter(it => !(keyFn(it) in dataToRemoveIndexed));
}
we introduce the indexBy here, to make removal O(m+n), instead of O(m^2) (if there are many items in the collection to check)
then you can use it like this:
const targetIndexed = indexBy(it => it.name, targetAry);
const result = sourceAry.map(
it => ({
...it,
children: remove(
it => it.name,
(targetIndexed[it.name] || {}).children || [],
it.children
)
})
)
so it leaves you with the following result:
[
{"name":"Label1","value":"label1","children":[{"name":"Ammu"}, {"name":"Rahual"}]},
{"name":"Label2","value":"label2","children":[]}
]
if you also want to delete the item with empty children, you can just filter it out: result.filter(it => it.children.length > 0)
Ciao, try something like this:
sourceAry = [{name:'Label1', value: 'label1', children:[{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]}, {name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'},{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]}, {name:'Label3', value: 'label3', children:[{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]} ];
targetAry = [{name:'Label1', value: 'label1', children:[{name:'Anu'}]},
{name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'}]},
];
let result = [];
sourceAry.forEach(source => {
let filter = targetAry.filter(target => target.name === source.name)
if (filter.length > 0) {
let filterchildren = source.children.filter(a => !filter[0].children.map(b=>b.name).includes(a.name));
if (filterchildren.length > 0) {
let resultobj = source;
resultobj.children = filterchildren;
result.push(resultobj);
}
}
else result.push(source);
})
console.log(result)
I filter targetAry based on sourceAry name. Then subtract children with .filter(a => !filter[0].children.map(b=>b.name).includes(a.name)); and finally push element found in result array.
I have a json with "proceds" and "teams".
Id´like to transform it grouping it by team.
Next, I show a snippet what I want achieve.
How can I do it?
dados:{
proceds:[
{id:'1', proc:'11', teams:[{team_id:1},{team_id:2}]},
{id:'2', proc:'12', teams:[{team_id:1},{team_id:3}]},
{id:'3', proc:'13', teams:[{team_id:2},{team_id:3}]},
]
}
I´d like to transform "dados" to:
dados:{
teams:[
{team_id:1, proceds:[{id:'1', proc:'11'},{id:'2', proc:'12'}],
{team_id:2, proceds:[{id:'1', proc:'11'},{id:'3', proc:'13'}]
{team_id:3, proceds:[{id:'2', proc:'12'},{id:'3', proc:'13'}]
]
}
You can create an object lookup for each team id and then generate the result using Object.entries and array#map.
const data = [ {id:'1', proc:'11', teams:[{team_id:1},{team_id:2}]}, {id:'2', proc:'12', teams:[{team_id:1},{team_id:3}]}, {id:'3', proc:'13', teams:[{team_id:2},{team_id:3}]}, ],
result = Object.entries(data.reduce((r, o) => {
o.teams.forEach(({ team_id }) => {
r[team_id] = r[team_id] || [];
r[team_id].push({ id: o.id, proc: o.proc });
});
return r;
}, {})).map(([team_id, proceds]) => ({ team_id, proceds }));
console.log(result);
.as-console-wrapper{min-height:100% !important; top: 0; }
You need to iterate each object to make groups.
let dados = {
proceds:[
{id:'1', proc:'11', teams:[{team_id:1},{team_id:2}]},
{id:'2', proc:'12', teams:[{team_id:1},{team_id:3}]},
{id:'3', proc:'13', teams:[{team_id:2},{team_id:3}]},
]
};
const groupByTeams = [];
dados.proceds.forEach(item=> {
let {teams, ...teamItems} = item;
teams.forEach(team=>{
groupByTeams.find(x=>x.team_id==team.team_id)?groupByTeams.find(x=>x.team_id==team.team_id).proceds.push({...teamItems}):groupByTeams.push({...team, proceds: [{...teamItems}]});
});
});
console.log({dados:{teams:groupByTeams}});
You can combine reduce with Object.keys
const asObject = dados.proceds.reduce((acc, curr) => {
curr.teams.forEach(t => acc[t.team_id] = [...(acc[t.team_id] || []), { id: curr.id, proc: curr.proc }])
return acc
}, {})
const result = {
teams: Object.keys(asObject).map(k => ({ team_id: k, proceds: asObject[k] }))
}
const dados = {
proceds:[
{id:'1', proc:'11', teams:[{team_id:1},{team_id:2}]},
{id:'2', proc:'12', teams:[{team_id:1},{team_id:3}]},
{id:'3', proc:'13', teams:[{team_id:2},{team_id:3}]},
]
}
function transform(object) {
object.teams = object.proceds.map((obj) => ({
team_id: parseInt(obj.id),
proceds: [
...obj.teams.map(team => ({
id: team.team_id.toString(),
proc: object.proceds.find(proc => proc.id == team.team_id).proc
}))
]
}))
delete object.proceds;
return object;
}
console.log(transform(dados))
Convert dot notation strings to array objects,
Eg.,
let obj = { 'user-0-address-pincode': 665766, 'user-0-address-city': 'Chennai', 'user-1-address-pincode': 32432, 'user-1-address-city': 'Bangalore'};
// Expectation output will be
{
user: [
{
address: {pincode: 665766, city: 'Chennai'}
},
{
address: {pincode: 32432, city: 'Bangalore'}
}
]
}
Please help me to resolve this problem.
You can use reduce and split methods to create a function that will take your object with keys as paths and then based on those keys create nested structure.
let obj = {
'user-0-address-pincode': 665766,
'user-0-address-city': 'Chennai',
'user-1-address-pincode': 32432,
'user-1-address-city': 'Bangalore'
};
function parse(data) {
return Object.keys(obj).reduce((r, k) => {
k.split('-').reduce((a, e, i, arr) => {
const next = arr[i + 1]
if (!next) return a[e] = data[k]
else return a[e] || (a[e] = (!isNaN(+next) ? [] : {}))
}, r)
return r;
}, {})
}
const result = parse(obj)
console.log(result)