Manipulate Object to group based on Array Object List - javascript

I'm trying to find a way to convert this list of objects based on the group array. The tricky part I've found is iterating through the group Array and applying the object to more than one place if there are multiple groups.
I'm also trying to ignore any group that does not belong to anything. I've tried using the reduce function but I cannot get the iteration through the group array.
let cars =
[
{
"group":[],
"name": "All Makes",
"code": ""
},
{
"group":["Group A"],
"name": "BMW",
"code": "X821"
},
{
"group":["Group B"],
"name": "Audi",
"code": "B216"
},
{
"group":["Group B"],
"name": "Ford",
"code": "P385"
},
{
"group":["Group B", "Group C"],
"name": "Mercedes",
"code": "H801"
},
{
"group":["Group C"],
"name": "Honda",
"code": "C213"
}
]
To become this:
[
{
"group": "Group A",
"cars": [
{
name: "BMW",
code: "X821"
}
]
},
{
"group": "Group B",
"cars": [
{
name: "Audi",
code: "B216"
},
{
name: "Ford",
code: "P385"
},
{
name: "Mercedes",
code: "H801"
}
]
},
{
"group": "Group C",
"cars": [
{
name: "Mercedes",
code: "H801"
},
{
name: "Honda",
code: "C213"
}
]
}
]
I've already tried using reduce to accomplish this but it doesn't quite get the desired affect.
const res = cars.reduce((acc, {group, ...r}) => {
group.forEach(key => {
acc[key] = (acc[key] || []).concat({...r});
});
return acc;
}, []);
console.log(res);

Using string keys on arrays doesn't work quite well, that's what objects are for, then use Object.values to get the array out of it. Also you want to put objects and not arrays in there:
const res = Object.values(cars.reduce((acc, {group, ...r}) => {
group.forEach(key => {
(acc[key] = (acc[key] || { group, cars: [] })).cars.push(r);
});
return acc;
}, {}));
I'd write it this way:
const byGroup = new Map();
for(const { group, ...rest } of cars) {
if(!byGroup.has(group))
byGroup.set(group, { group, cars: [] });
byGroup.get(group).cars.push(rest);
}
const result = [...byGroup.values()];

You need to take an object as accumulator and then map the entries fro getting the wanted result.
let cars = [{ group: [], name: "All Makes", code: "" }, { group: ["Group A"], name: "BMW", code: "X821" }, { group: ["Group B"], name: "Audi", code: "B216" }, { group: ["Group B"], name: "Ford", code: "P385" }, { group: ["Group B", "Group C"], name: "Mercedes", code: "H801" }, { group: ["Group C"], name: "Honda", code: "C213" }],
result = Object
.entries(cars.reduce((acc, { group, ...r }) => {
group.forEach(key => acc[key] = (acc[key] || []).concat({...r}));
return acc;
}, {}))
.map(([group, cars]) => ({ group, cars }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

JavaScript - filter in loop, create duplicate

I want to filter an array with another array in order to know if there are new people.
const people = [
{ "name": "jerry" },
{ "name": "tom" },
{ "name": "alex" }
]
const newList = [
{ "name": "bran" },
{ "name": "jerry" },
{ "name": "john" }
]
const new_people = []
for (const pp of people) {
let result = newList.filter(newL => newL.name != pp.name)
if (result) {
new_people.push(result)
}
}
console.log(new_people)
This is the result:
[
[ { name: 'bran' }, { name: 'john' } ],
[ { name: 'bran' }, { name: 'jerry' }, { name: 'john' } ],
[ { name: 'bran' }, { name: 'jerry' }, { name: 'john' } ]
]
But I'm looking for:
[ { name: 'bran' }, { name: 'john' } ]
I would like to avoid the loop because it makes duplicate in the result but I don't know how I can't do it without the loop.
First make a temporary array of people name:
const peopleNames = people.map(pp => pp.name);
Then the peopleNames will be as follows:
['jerry', 'tom', 'alex']
Now filter the new people from the newList:
const newPeople = newList.filter(pp => !peopleNames.includes(pp.name));
The newPeople will be an array of objects that you are looking for.
[{name: 'bran'}, {name: 'john'}]
const people = [
{ "name": "jerry" },
{ "name": "tom" },
{ "name": "alex" }
]
const newList = [
{ "name": "bran" },
{ "name": "jerry" },
{ "name": "john" }
]
const output = newList.filter(a => people.filter(x => x.name == a.name).length == 0);
console.log('By USing Filter', output);
//filter the newList and retain those object, which are not present in the people
//Way 2: By using some
//const output2 = newList.filter(a => !people.some(x => x.name == a.name));
//console.log('By Using Some', output2);
You can use Array's reduce method to get the desired result:
const new_people = newList.reduce((accVal, e) =>
(people.map(p => p.name).includes(e.name))
? accVal
: accVal.concat({ "name": e.name } ),
[ ] )

New Map from JSON data with unique values

I'm trying to create a new map of [key:value] as [trait_type]:[{values}]
So taking the below data it should look like:
[Hair -> {"Brown", "White-Blue"},
Eyes -> {"Green", "Red"}
]
Heres my json obj
[
{
"name":"Charlie",
"lastname":"Gareth",
"date":1645462396133,
"attributes":[
{
"trait_type":"Hair",
"value":"Brown"
},
{
"trait_type":"Eyes",
"value":"Red"
}
]
},
{
"name":"Bob",
"lastname":"James",
"date":1645462396131,
"attributes":[
{
"trait_type":"Hair",
"value":"White-Blue"
},
{
"trait_type":"Eyes",
"value":"green"
}
]
}
];
To note: I'm also trying to make it the values unique so If I have 2 people with red eyes the value would only ever appear once.
This is what I have so far, kind of works but in the console window the values come out as a char array.
let newData = data.map((item) =>
item.attributes.map((ats) => {
return { [ats.trait_type]: [...ats.value] };
})
);
newData.map((newItem) => console.log(newItem));
You could take an object and iterate the nested data with a check for seen values.
const
data = [{ name: "Charlie", lastname: "Gareth", date: 1645462396133, attributes: [{ trait_type: "Hair", value: "Brown" }, { trait_type: "Eyes", value: "Red" }] }, { name: "Bob", lastname: "James", date: 1645462396131, attributes: [{ trait_type: "Hair", value: "White-Blue" }, { trait_type: "Eyes", value: "green" }] }],
result = data.reduce((r, { attributes }) => {
attributes.forEach(({ trait_type, value }) => {
r[trait_type] ??= [];
if (!r[trait_type].includes(value)) r[trait_type].push(value);
})
return r;
}, {});
console.log(result);
try this
const extractTrait = data =>
data.flatMap(d => d.attributes)
.reduce((res, {
trait_type,
value
}) => {
return {
...res,
[trait_type]: [...new Set([...(res[trait_type] || []), value])]
}
}, {})
const data = [{
"name": "Charlie",
"lastname": "Gareth",
"date": 1645462396133,
"attributes": [{
"trait_type": "Hair",
"value": "Brown"
},
{
"trait_type": "Eyes",
"value": "Red"
}
]
},
{
"name": "Bob",
"lastname": "James",
"date": 1645462396131,
"attributes": [{
"trait_type": "Hair",
"value": "White-Blue"
},
{
"trait_type": "Eyes",
"value": "green"
}
]
}
];
console.log(extractTrait(data))

How to group from array object

I using code form "
I am looking for best ways of doing this. I have group:
data
[
{
"date": "16/04/2020",
"count": 0,
"name": "A"
},
{
"date": "16/04/2020",
"count": 1,
"name": "B"
},
{
"date": "17/04/2020",
"count": 0,
"name": "B"
}
//...More.....
]
Answer
{
"date": "04/2020",
"symtom": {
"data": [
{
"date": "16/04/2020",
"data": [
{
"name": "A",
"count": [
{
"date": "16/04/2020",
"count": 0,
"name": "A"
}
]
},
{
"name": "B",
"count": [
{
"date": "16/04/2020",
"count": 1,
"name": "B"
}
]
},
//...More.....
]
},
{
"date": "17/04/2020",
"data": [
{
"name": "B",
"count": [
{
"date": "17/04/2020",
"count": 0,
"name": "B"
}
]
},
//...More.....
]
}
]
}
}
Can I fix the code and to get the desired answer?
Code :
const items = [
{
tab: 'Results',
section: '2017',
title: 'Full year Results',
description: 'Something here',
},
{
tab: 'Results',
section: '2017',
title: 'Half year Results',
description: 'Something here',
},
{
tab: 'Reports',
section: 'Marketing',
title: 'First Report',
description: 'Something here',
}
];
function groupAndMap(items, itemKey, childKey, predic){
return _.map(_.groupBy(items,itemKey), (obj,key) => ({
[itemKey]: key,
[childKey]: (predic && predic(obj)) || obj
}));
}
var result = groupAndMap(items,"tab","sections",
arr => groupAndMap(arr,"section", "items"));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
ref : Group array of object nesting some of the keys with specific names
But I would like to have the answer line this (Answer) :
{
"date": "04/2020",
"symtom": {
"data": [
{
"date": "16/04/2020",
"data": [
{
"name": "A",
"count": 0,
},
{
"name": "B",
"count": 1,
},
//...More.....
]
},
{
"date": "17/04/2020",
"data": [
{
"name": "B",
"count":0,
},
//...More.....
]
}
]
}
}
Thanks!
I am a beginner but it looks like you want system.data.data to = an array of objects with the keys name:str and count:number but instead you are applying the whole object into count so the key count:{name:A, count:0,date:etc}.
I really can't follow your function which separates the data... but all you should have to do is when count is sent the object to reference just do a dot notation like object.count to access the number vs the object that way you will have the desired affect. Hopefully that is what you were asking.
I would use a helper function groupBy (this version is modeled after the API from Ramda [disclaimer: I'm one of its authors], but it's short enough to just include here.) This takes a function that maps an object by to a key value, and then groups your elements into an object with those keys pointing to arrays of your original element.
We need to use that twice, once to group by month and then inside the results to group by day. The rest of the transform function is just to format your output the way I think you want.
const groupBy = (fn) => (xs) =>
xs .reduce((a, x) => ({... a, [fn(x)]: [... (a [fn (x)] || []), x]}), {})
const transform = (data) =>
Object .entries (groupBy (({date}) => date.slice(3)) (data)) // group by month
.map (([date, data]) => ({
date,
symtom: {
data: Object .entries (groupBy (({date}) => date) (data)) // group by day
.map (([date, data]) => ({
date,
data: data .map (({date, ...rest}) => ({...rest})) // remove date property
}))
}
}))
const data = [{date: "16/04/2020", count: 0, name: "A"}, {date: "16/04/2020", count: 1, name: "B"}, {date: "17/04/2020", count: 0, name: "B"}, {date: "03/05/2020", count: 0, name: "C"}];
console .log (
transform (data)
)
.as-console-wrapper {min-height: 100% !important; top: 0}
If you need to run in an environment without Object.entries, it's easy enough to shim.
You could take a function for each nested group and reduce the array and the grouping levels.
var data = [{ date: "16/04/2020", count: 0, name: "A" }, { date: "16/04/2020", count: 1, name: "B" }, { date: "17/04/2020", count: 0, name: "B" }],
groups = [
(o, p) => {
var date = o.date.slice(3),
temp = p.find(q => q.date === date);
if (!temp) p.push(temp = { date, symptom: { data: [] } });
return temp.symptom.data;
},
({ date }, p) => {
var temp = p.find(q => q.date === date);
if (!temp) p.push(temp = { date, data: [] });
return temp.data;
},
({ date, ...o }, p) => p.push(o)
],
result = data.reduce((r, o) => {
groups.reduce((p, fn) => fn(o, p), r);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Grouping array data according to a id

With a API call i'm receiving a response like below
[
{
stationId: "10"
name: "Jinbaolai"
group: {id: "18", stationGroupName: "Ali"}
},
{
stationId: "13"
name: "Stack"
group: {id: "18", stationGroupName: "Ali"}
},
{
stationId: "20"
name: "Overflow"
group: {id: "19", stationGroupName: "Baba"}
}
]
As you can see first two records consist with the same group. I want to group these data according to the group. So for example it should look like this
[
{
groupId: "18",
groupName : "Ali",
stations : [
{
stationId: "10",
name: "Jinbaolai"
},
{
stationId: "13",
name: "Stack"
}
]
},
{
groupId: "19",
groupName : "Baba",
stations : [
{
stationId: "20",
name: "Overflow"
},
]
}
]
I want to do the grouping logic in my reducer where i also set the full data array that is shown in the beginning of the question.
case EVC_SUCCESS:
return {
...state,
chargingStations: action.evcData.chargingStations,
chargingStationGroups: //This is where my logic should go. ('action.evcData.chargingStations' is the initial data array)
tableLoading: false
}
How can i do this? I tried something using filter but not successful.
The best way to do this is to use Array.prototype.reduce()
Reduce is an aggregating function where you put in an array of something and get a single vaule back.
There may be a starting value as last parameter like I used {}.
The signature is reduce(fn, startingValue) where fn is a function taking two parameters aggregate and currentValue where you return the aggregate in the end.
const groupData = (data)=> {
return Object.values(data.reduce((group,n)=>{
if (!group[n.group.id]){
group[n.group.id] = {
groupId:n.group.id,
groupName: n.group.stationGroupName,
stations:[]}
}
group[n.group.id].stations.push({
stationID: n.stationId,
name: n.name
})
return group;
}, {}))
}
Here is the fiddle
A simple JS algorithm can do that for you
const list = [
{
stationId: "10",
name: "Jinbaolai",
group: {id: "18", stationGroupName: "Ali"}
},
{
stationId: "13",
name: "Stack",
group: {id: "18", stationGroupName: "Ali"}
},
{
stationId: "20",
name: "Overflow",
group: {id: "19", stationGroupName: "Baba"}
}
];
const groups = {};
list.forEach((item) => {
const groupId = item.group.id;
const group = groups[groupId] || {groupId: groupId, groupName: item.group.stationGroupName, stations: []};
group.stations.push({stationId: item.stationId, name: item.name});
groups[groupId] = group;
});
const groupedArray = Object.keys(groups).map((groupId) => groups[groupId]);
console.log(groupedArray); // This will be the output you want
I think chaining multiple functions will work.
const stations = [
{
"stationId": 10,
"name": "Jinbaolai",
"group": {"id": "18", "stationGroupName": "Ali"}
},
{
"stationId": 13,
"name": "Stack",
"group": {"id": 18, "stationGroupName": "Ali"}
},
{
"stationId": 20,
"name": "Overflow",
"group": {"id": "19", "stationGroupName": "Baba"}
}
]
const groups = _.chain(stations)
.groupBy((station) => { return station.group.id })
.map((values, key) => {
return {
"groupId": _.first(values).group.id,
"groupName": _.first(values).group.id,
"stations": _.map(values,(value)=>{ return { "stationId": value.stationId, "name": value.name } })
}
})
console.log("groups",groups)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>

how to merge arrays which contains objects?

I have more than two arrays with objects which have some similar properties,
and I want to merge all arrays to one array and sort theme as bellow:
array = [
[ { "name" : "Jack", count: 10 }, { "name": "Fred", "count": 20 } ],
[ { "name": "Jack", count: 3 }, { "name": "Sara", "count": 10 } ]
]
merged_array = [
{ "name": "Fred", "count": 20 },
{ "name": "Jack", "count": 13 },
{ "name": "Sara", "count": 10 }
]
array
.reduce(function(result, arrayItem) {
result = result || [];
// sort also can be implemented here
// by using `Insertion sort` algorithm or anyone else
return result.concat(arrayItem);
})
.sort(function(a,b) { return a.count < b.count; })
You can use two forEach() to get array with merged objects and then sort it with sort().
var array = [
[{
"name": "Jack",
count: 10
}, {
"name": "Fred",
"count": 20
}],
[{
"name": "Jack",
count: 3
}, {
"name": "Sara",
"count": 10
}]
]
var result = [];
array.forEach(function(a) {
var that = this;
a.forEach(function(e) {
if (!that[e.name]) {
that[e.name] = e;
result.push(that[e.name]);
} else {
that[e.name].count += e.count
}
})
}, {})
result.sort(function(a, b) {
return b.count - a.count;
})
console.log(result)
If you are open to, then do take a look at to underscorejs, it is a wonderful utility for working with Arrays and objects as such. This will allow you to write flexible and transparent logic. The documentation is also quite good.
var arrays = [
[{
"name": "Jack",
count: 10
}, {
"name": "Fred",
"count": 20
}],
[{
"name": "Jack",
count: 3
}, {
"name": "Sara",
"count": 10
}]
]
var result = _.chain(arrays)
.flatten()
.union()
// whatever is the "key" which binds all your arrays
.groupBy('name')
.map(function(value, key) {
var sumOfCount = _.reduce(value, function(entry, val) {
// the fields which will need to be "aggregated"
return entry + val.count;
}, 0);
return {
'name': key,
'count': sumOfCount
};
}
)
// you can have your own sorting here
.sortBy('count')
.value();
console.log(result.reverse());
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Categories

Resources