React Map Array into a complex object [duplicate] - javascript

This question already has answers here:
Group array items using object
(19 answers)
Closed 3 days ago.
As a react apprentice, I'm having a hard time mapping data into a new object grouping by it states, I´ve tried example_1 and example_2 with no success, also saw a lot of simple mapping questions that doesn't apply into my case.
My App.js:
import "./styles.css";
export default function App() {
const data = [
{
"id": 1,
"name": "Chris",
"state": "MA",
"stateId": 1
},
{
"id": 2,
"name": "Jenna",
"state": "MA",
"stateId": 1
},
{
"id": 3,
"name": "Pat",
"state": "RI",
"stateId": 2
},
{
"id": 4,
"name": "Dan",
"state": "RI",
"stateId": 2
}
];
const newdata = data.map((comp) => ({
stateId: comp.stateId,
state: comp.state,
person: [{ id: comp.id, name: comp.name }]
}));
console.log(newdata)
return (
<div className="App">
<h2>
<h1>test</h1>
</h2>
</div>
);
}
I would like to have something like this:
[
{
"stateId": 1,
"state": "MA",
"person": [
{
"id": "1",
"name": "chris"
},
{
"id": "2",
"name": "Jenna"
}
]
},
{
"stateId": 2,
"state": "RI",
"person": [
{
"id": "3",
"name": "Pat"
},
{
"id": "4",
"name": "Dan"
}
]
}
]

You can use reduce for hard mapping:
const result = data.reduce((acc, value, index, array) => {
const result = [...acc];
// Find index to know is state exist in acc and what index state has
const stateIndex = acc.findIndex((v) => v.stateId === value.stateId);
// Person object to use in different conditions
const person = {
id: value.id,
name: value.name
};
// If state dont exist - push to result array
if (stateIndex < 0) {
result.push({
stateId: value.stateId,
state: value.state,
person: [
{
id: value.id,
name: value.name
}
]
});
// If state exist - add person
} else {
result[stateIndex] = {
...result[stateIndex],
person: result[stateIndex]?.person
? [...result[stateIndex].person, person]
: [person]
};
}
return result;
}, []);
Looks like it works
https://codesandbox.io/s/fervent-varahamihira-gfymx8?file=/src/index.js

Related

How to filtering out the multiple nested object in Javascript object

Javascript
I have a nested array of objects, I'm trying to filter the given array of objects using a property from the third level of its array property value. For example, from the below array I like to filter the entire array using the property ListId: 10
Example
let test = {
"test":true,
"group":[
{
"name":"header",
"value":[
{
"id":"0",
"list":[
{
"ListId":10,
"name":"string1",
"state":"BY",
"techId":0
},
{
"ListId":11,
"name":"string2",
"state":"BY"
},
{
"ListId":12,
"name":"string3",
"state":"BY"
}
]
}
]
},
{
"name":"header2",
"value":[
{
"id":"01",
"list":[
{
"ListId":100,
"name":"string1",
"state":"BY",
"techId":0
},
{
"ListId":111,
"name":"string2",
"state":"BY"
},
{
"ListId":121,
"name":"string3",
"state":"BY"
}
]
}
]
}
]
}
Filtervalue with ListId = 10
Expected output :
{
"test":true,
"group":[
{
"name":"header",
"value":[
{
"id":"0",
"list":[
{
"ListId":10,
"name":"string1",
"state":"BY",
"techId":0
}
]
}
]
}
]
}
How can I use the filter method using javascript to get this expected result?
You can two it in two times :
First, filter the list arrays,
Secondly filter the groups array using the some method
let test= {
"test": true,
"group": [
{
"name": "header",
"value": [
{
"id": "0",
"list": [
{
"ListId": 10,
"name": "string1",
"state": "BY",
"techId": 0
},
{
"ListId": 11,
"name": "string2",
"state": "BY"
},
{
"ListId": 12,
"name": "string3",
"state": "BY"
}
]
}
]
},
{
"name": "header2",
"value": [
{
"id": "01",
"list": [
{
"ListId": 100,
"name": "string1",
"state": "BY",
"techId": 0
},
{
"ListId": 111,
"name": "string2",
"state": "BY"
},
{
"ListId": 121,
"name": "string3",
"state": "BY"
}
]
}
]
}
]
}
test.group.forEach(group => {
group.value.forEach(value => {
value.list = value.list.filter(list => list.ListId === 10)
})
})
test.group = test.group.filter(group => group.value.some(value => value.list.length > 0))
console.log(test)
Note : You should use plural names for you arrays, it helps understanding the data. For example lists not list for the array.
let z ={"group1": [
{
"name": "header",
"value": [
{
"id": 0,
"list": [
{
"ListId": 10,
"Name": "string1"
},
{
"ListId": 11,
"Name": "string2"
}
]
}
]
}
]}
// This function was written from understading that 'group1' is not a fixed property, but part of a dynamic list due to the number '1'
const getItemByListId = (list, listId) => {
const listKeys = Object.keys(list);
const selectedListKey = listKeys.find(key => {
const groupItems = list[key];
const selectedItem = groupItems.find(({ value: nestedItems }) => {
const selectedNestedItem = nestedItems.find(({ list }) => {
const selectedList = list.find(({ ListId }) => ListId === listId)
return selectedList;
});
return selectedNestedItem;
});
return selectedItem;
});
if (!selectedListKey) {
return null;
}
return list[selectedListKey];
};
console.log(getItemByListId(z, 10));

Merge two objects only if id in object 2 exists in object 1 in plain Javascript

Got two objects:
master = [ { "id": 411, "state": 1 },
{ "id": 2134, "state": 1 },
{ "id": 2135, "state": 0 },
{ "id": 2137, "state": 0 } ]
zips = [ { "id": 411, "zip": "90201" },
{ "id": 412, "zip": "90201" },
{ "id": 2134, "zip": "90201" },
{ "id": 2137, "zip": "90201" } ]
I'd like to merge them together only if the id in zips exists in devices. Using the above data, the result should be:
master = [ { "id": 411, "state": 1, "zip": "90201" },
{ "id": 2134, "state": 1, "zip": "90201" },
{ "id": 2135, "state": 0 },
{ "id": 2137, "state": 0, "zip": "90201" } ]
id: 412 is zips is 'skipped' because it's not in the devices object.
I've looked at looping through functions like Object.assign or merging with ... , but they have undesirable results (like adding the id 412 above because it exists in the second object)
Any hints or tips or better places to look are greatly appreciated!
You can easily achieve the result using map
const result = master.map((obj) => ({
...obj,
...zips.find((o) => o.id === obj.id),
}));
master = [
{ id: 411, state: 1 },
{ id: 2134, state: 1 },
{ id: 2135, state: 0 },
{ id: 2137, state: 0 },
];
zips = [
{ id: 411, zip: "90201" },
{ id: 412, zip: "90201" },
{ id: 2134, zip: "90201" },
{ id: 2137, zip: "90201" },
];
const result = master.map((obj) => {
const { id } = obj;
const objThatExist = zips.find((o) => o.id === id);
return { ...obj, ...objThatExist };
});
console.log(result);
I solve this with nested each. I look every master objects and control that there are any same ids. if they are same values , push the zip into master object
jQuery
$.each(zips,(index,object)=>{
var zipID=object.id;
$.each(master,(indexMaster,objectMaster)=>{
if(zipID==objectMaster.id){
objectMaster['zip']=zipID
}
})
})
console.log(master);
JavaScript
zips.forEach(index,object){
var zipID=object.id;
master.forEach(indexMaster,objectMaster){
if(zipID==objectMaster.id){
objectMaster['zip']=zipID
}
}
}

How do I remove an object if it contains a key with an empty array?

I have an an array of objects. My objective is to remove objects that contain keys with empty arrays.
I am using ramda, but am hitting a wall at the moment.
const myData = {
"one": {
"two": {
"id": "1",
"three": [{
"id": "33",
"copy": [{
"id": "1",
"text": "lorem",
"answer": [],
},
{
"id": "2",
"text": "ipsum",
"answer": [{
"id": 1,
"class": "science"
}]
},
{
"id": "3",
"text": "baesun",
"answer": [{
"id": 2,
"class": "reading"
}]
}
],
}]
}
}
}
flatten(pipe(
path(['one', 'two', 'three']),
map(step => step.copy.map(text => ({
answers: text.answer.map(answer => ({
class: answer.class,
})),
}), ), ))
(myData))
This the result:
[{"answers": []}, {"answers": [{"class": "science"}]}, {"answers": [{"class": "reading"}]}]
This is the expectation:
[{"answers": [{"class": "science"}]}, {"answers": [{"class": "reading"}]}]
Get the the array of inside three with path, chain the arrays inside the copy properties, and project them to contain only answer. Reject empty answers, and then evolve the objects inside each answer to contain only the class property.
const {pipe, path, chain, prop, project, reject, propSatisfies, isEmpty, map, evolve} = ramda
const transform = pipe(
path(['one', 'two', 'three']), // get the array
chain(prop('copy')), // concat the copy to a single array
project(['answer']), // extract the answers
reject(propSatisfies(isEmpty, 'answer')), // remove empty answers
map(evolve({ answer: project(['class']) })) // convert the objects inside each answer to contain only class
)
const data = {"one":{"two":{"id":"1","three":[{"id":"33","copy":[{"id":"1","text":"lorem","answer":[]},{"id":"2","text":"ipsum","answer":[{"id":1,"class":"science"}]},{"id":"3","text":"baesun","answer":[{"id":2,"class":"reading"}]}]}]}}}
const result = transform(data)
console.log(result)
<script src="//bundle.run/ramda#0.26.1"></script>
use filter
const filter = R.filter,
flatten = R.flatten,
pipe = R.pipe,
path = R.path,
map = R.map;
const myData = {
"one": {
"two": {
"id": "1",
"three": [{
"id": "33",
"copy": [{
"id": "1",
"text": "lorem",
"answer": [],
},
{
"id": "2",
"text": "ipsum",
"answer": [{
"id": 1,
"class": "science"
}]
},
{
"id": "3",
"text": "baesun",
"answer": [{
"id": 2,
"class": "reading"
}]
}
],
}]
}
}
}
const result = filter(answersObj => answersObj.answers.length, flatten(pipe(
path(['one', 'two', 'three']),
map(step => step.copy.map(text => ({
answers: text.answer.map(answer => ({
class: answer.class,
}))
})))
)(myData)))
console.log(result);
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Build tree from flat array using two data tables in Javascript

I'm stuck with creating tree structure from a flat array using two Mock data tables in JSON.
the table should match the two unique id to determine the hierarchy between them.
JSON with Groups DB array looks like that:
{
"group": [
{
"groupName": "ROOT",
"id": 1
},
{
"groupName": "Family",
"id": 9
},
{
"groupName": "BestFriends!",
"id": 10
},
{
"groupName": "Cars",
"id": 4
},
{
"groupName": "funHouse",
"id": 3
}
]
};
JSON including Users array looks like that:
{
"user": [
{
"username": "StrongGoose",
"password": "sdff12fdsa",
"age": 31,
"id": 2
},
{
"username": "John",
"password": "sdjd34fffdsa",
"age": 31,
"id": 3
},
{
"username": "Mary",
"password": "sdfffdsa",
"age": 31,
"id": 4
}
]
};
this is how is the first data table looks like and determines the hierarchy between groups:
{
"GroupsToGroups": [
{
"1":[9,10]
},
{
"10":[3]
}
]
};
The second looks like that and determines which user belongs to which group:
{
"GroupsToUsers": [
{
"11":[2]
},
{
"3":[3]
},
{
"4":[4]
},
{
"10":[2]
},
{
"3":[3]
}
]
};
The Hierarchy should look like that, needs to be written to JSON
[
{
"type": "group",
"id": "1",
"name": "ROOT",
"items": [
{
"type": "group",
"id": "9",
"name": "Family",
"items": []
},
{
"type": "group",
"id": "10",
"name": "BestFriends!",
"items": [
{
"username": "StrongGoose",
"password": "sdff12fdsa",
"age": 31,
"id": 2
},
{
"type": "group",
"id": "3",
"name": "funHouse",
"items": [
{
"username": "John",
"password": "sdjd34fffdsa",
"age": 31,
"id": 3
},
{
"type": "group",
"id": "4",
"name": "Cars",
"items": [
{
"username": "Mary",
"password": "sdfffdsa",
"age": 31,
"id": 4
}
],
}
]
}
]
}
]
}
];
edit: i have tried to create a function with a recursion that finds the relevant related groups.
it works but i don't know how to combine the users.
function checkChildren(group) {
const allChildren = insideGroups[group.id];
if (!allChildren) return group;
const childGroups = allChildren.map((findChildrenID) => {
const indexGroups = groups.findIndex((subGroup) => subGroup.id ===
findChildrenID);
return checkChildren(groups[indexGroups]);
});
return Object.assign({}, group, {groups: childGroups});
}
You could take a hash table for the various types of data for a faster accesss without iterating the arrays of objects.
In the case of users, you need anyway a new object with new properties and renamed keys.
Then you need a new property for the root objects and add it to the groups.groups property to have the same access type for all levels.
Ath the end iterate first groups.users and then groups.groups to get all object and for the groups, take the children as well.
In the given data I commented the unused/duplicate data.
function getNodes(node) {
return [
...(hash.groups.users[node] || []).map(id => hash.user[id]),
...(hash.groups.groups[node] || []).map(id => Object.assign(hash.group[id], { children: getNodes(id) }))
];
}
var db = {
group: [
{ groupName: "ROOT", id: 1 },
{ groupName: "Family", id: 9 },
{ groupName: "BestFriends!", id: 10 },
{ groupName: "Cars", id: 4 },
{ groupName: "funHouse", id: 3 }
],
user: [
{ username: "StrongGoose", password: "sdff12fdsa", age: 31, id: 2 },
{ username: "John", password: "sdjd34fffdsa", age: 31, id: 3 },
{ username: "Mary", password: "sdfffdsa", age: 31, id: 4 }
],
GroupsToGroups: [
{ 1: [9, 10] }, // ok
{ 10: [3] }, // second
{ 3: [4] }
],
GroupsToUsers: [
//{ 11: [2] }, // never used
{ 3: [3] },
{ 4: [4] },
{ 10: [2] }, // first
//{ 3: [3] } // dupe
]
},
hash = {
group: Object.assign(...db.group.map(({ id, groupName: name, type = 'group' }) => ({ [id]: { type, id, name } }))),
user: Object.assign(...db.user.map(o => ({ [o.id]: o }))),
groups: {
groups: Object.assign(...db.GroupsToGroups, { root: db.group.filter(({ groupName }) => groupName === 'ROOT').map(({ id }) => id) }),
users: Object.assign(...db.GroupsToUsers)
}
},
result = getNodes('root');
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Fetch the no of of occurence in a array based on other key value

var json = [{
"city": "California",
"name": "Joe",
"age": 17,
"type",:"custom"
}, {
"city": "California",
"name": "Bob",
"age": 17,
"type",:"predefined"
}, {
"city": "California",
"name": "Bob",
"age": 35,
"type",:"custom"
}, {
"city": "Texas",
"name": "Bob",
"age": 35,
"type",:"custom"
}, {
"city": "Florida",
"name": "Bob",
"age": 35,
"type",:"predefined"
}];
I have above array and i have to construct object, based on "type" value i.e "predefined" or "custom" as below
updatedjson = {
"predefined": [{
"name": "California",
"count": 1
}, {
"name": "Florida",
"count": 1
}]
"custom": [{
"name": "California",
"count": 2
}, {
"name": "Texas",
"count": 1
}]
}
Any Approach using javascript or lodash
Using Lodash
var json = [{
"city": "California",
"name": "Joe",
"age": 17,
"type": "custom"
}, {
"city": "California",
"name": "Bob",
"age": 17,
"type": "predefined"
}, {
"city": "California",
"name": "Bob",
"age": 35,
"type": "custom"
}, {
"city": "Texas",
"name": "Bob",
"age": 35,
"type": "custom"
}, {
"city": "Florida",
"name": "Bob",
"age": 35,
"type": "predefined"
}];
// Solution to the problem
var updatedJson = _(json).groupBy("type").value(); // #1
_.forOwn(updatedJson, function(value, key) {
let countByCity = _(value).countBy('city').value(); // #2
let res = [];
_.forOwn(countByCity, function(value, key) {
res.push({ // #3
name: key,
count: value
});
});
updatedJson[key] = res;
});
console.log(updatedJson);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
Explanation
Code first groups by type
Then counts by cities, within the group
Finally pushes the count object into an array, in the expected format
You can use reduce & findIndex
var json = [{
"city": "California",
"name": "Joe",
"age": 17,
"type": "custom"
}, {
"city": "California",
"name": "Bob",
"age": 17,
"type": "predefined"
}, {
"city": "California",
"name": "Bob",
"age": 35,
"type": "custom"
}, {
"city": "Texas",
"name": "Bob",
"age": 35,
"type": "custom"
}, {
"city": "Florida",
"name": "Bob",
"age": 35,
"type": "predefined"
}];
var updatedjson = json.reduce(function(result, item) {
// check if type is present in the object
// if not present then add a key by name custom/predefined
if (!result[item.type]) {
result[item.type] = [];
// add city name and intial count
result[item.type].push({
name: item.city,
count: 1
})
} else {
// now find the index of the object whe name matches with current city
let m = result[item.type].findIndex(function(obj) {
return obj.name === item.city
})
// if no matches then add the new object to either custom/predefined
if (m === -1) {
result[item.type].push({
name: item.city,
count: 1
})
} else {
// if matches then increase the value of count
result[item.type][m].count += 1
}
}
return result;
}, {});
console.log(updatedjson)
You can use array.reduce to create a new object with the aggregated data. Example:
const result = json.reduce((obj, data) => {
// if this is a new type, we must initialize it as an empty array and push the first record.
if(!obj[data.type]) {
obj[data.type] = [];
obj[data.type].push({name: data.city, count: 1});
return obj;
}
// else ... check if the current city is present in that type
const existingRecord = obj[data.type].find(d => d.name === data.city);
if(!existingRecord) {
// if the city is not present in that type, we initialize it with count 1
obj[data.type].push({name: data.city, count: 1});
return obj;
}
// if the city is present, we just update count = count + 1;
existingRecord.count++;
return obj;
}, { })
Now, you probably want to extract some of that logic into separate functions to increase readability. I don't think you need lodash for this but in the case you already have it included in your project then feel free to use it :P

Categories