Complex sorting - javascript

I have many structures like:
student :
name: SomeName
city : someSity
school : someschool
date : somedate
What I want is to sort them in a way like (with test data):
Sorted_map:
city : NY
school: School1
name:Name1
name: Name2
name: Name4
shool: School2
name:Name11
name: Name21
name: Name41
city: WDC
school: School3
name: Name22
name: Name29
What I did is sorting by city :
function groupBy( array , f ) {
var groups = {};
array.forEach( function( o )
{
var group = JSON.stringify( f(o) );
groups[group] = groups[group] || [];
groups[group].push( o );
});
return groups;
}
var result = groupBy(sudentsItems, function(item)
{
return unifyCity(item.city);
}
so now it is sorted only by the "city" key. How do I sort data inside city?
I guess I need something like Map < city_key, Map < school_key, name > >;
Data sample :
[{
"name": "John Some",
"city": "NY",
"school": "NY Central",
"date": 1460986261733
}, {
"name": "Sarah Mil",
"city": "Moscow",
"school": "Moscow Central",
"date": 1460986201733
}, {
"name": "John Again",
"city": "NY",
"school": "NY Central",
"date": 1460986261733
}, {
"name": "Kit Parcer",
"city": "NY",
"school": "NY Central",
"date": 1460086261733
}, {
"name": "Anna Love",
"city": "SPB",
"school": "SPB Central",
"date": 1460983261733
}]

It looks like that you are looking either for sorted or a grouped data.
var data = [{ "name": "John Some", "city": "NY", "school": "NY Central", "date": 1460986261733 }, { "name": "Sarah Mil", "city": "Moscow", "school": "Moscow Central", "date": 1460986201733 }, { "name": "John Again", "city": "NY", "school": "NY Central", "date": 1460986261733 }, { "name": "Kit Parcer", "city": "NY", "school": "NY Central", "date": 1460086261733 }, { "name": "Anna Love", "city": "SPB", "school": "SPB Central", "date": 1460983261733 }],
object = {};
data.sort(function (a,b) {
return a.city.localeCompare(b.city) ||
a.school.localeCompare(b.school) ||
a.name.localeCompare(b.name);
});
data.forEach(function (a) {
object[a.city] = object[a.city] || {};
object[a.city][a.school] = object[a.city][a.school] || [];
object[a.city][a.school].push(a.name);
});
document.write('<pre>data sorted: ' + JSON.stringify(data, 0, 4) + '</pre>');
document.write('<pre>data grouped: ' + JSON.stringify(object, 0, 4) + '</pre>');

You can use a for-loop to iterate over all the fields in the sorters array. If, at any point, the result of the comparison is not 0 (equal) then immediately return the value.
function sortMulti(data, sorters) {
return data.sort(function(a, b) {
for (var i = 0; i < sorters.length; i++) {
var result = 0;
var f1 = a[sorters[i].field];
var f2 = b[sorters[i].field];
if (typeof f1 === 'string' || typeof f2 === 'string') {
result = f1.localeCompare(f2);
} else {
result = f1 < f2 ? -1 : f1 > f2 ? 1 : 0;
}
if (sorters[i].direction === 'desc') {
result = result * -1;
}
if (result !== 0) {
return result;
}
}
});
}
var data = [
{ "name": "John Some", "city": "NY", "school": "NY Central", "date": 1460986261733 },
{ "name": "Sarah Mil", "city": "Moscow", "school": "Moscow Central", "date": 1460986201733 },
{ "name": "John Again", "city": "NY", "school": "NY Central", "date": 1460986261733 },
{ "name": "Kit Parcer", "city": "NY", "school": "NY Central", "date": 1460086261733 },
{ "name": "Anna Love", "city": "SPB", "school": "SPB Central", "date": 1460983261733 }
];
var sorters = [
{ field: 'city', direction: 'asc' },
{ field: 'school', direction: 'asc' },
{ field: 'name', direction: 'asc' }
];
document.body.innerHTML = '<pre>' + JSON.stringify(sortMulti(data, sorters), null, 4) + '</pre>';

Related

Javascript group data and append additional data

I am looking to solve 2 problems here
Group the orders to create an items array based on based on orderNo
Include the tracking-information from the tracking array in tracking.json into the orders array.
So far I have only managed to get started with the grouping.
//orders.json
const orders = [
{
"orderNo": "1",
"tracking_number": "001",
"courier": "FedEx",
"street": "1745 T Street Southeast",
"zip_code": 20020,
"city": "Louisville",
"destination_country": "USA",
"email": "test#test.com",
"articleNo": "A",
"articleImageUrl": "watch.jpg",
"quantity": 1,
"product_name": "Watch"
},
{
"orderNo": "1",
"tracking_number": "001",
"courier": "FedEx",
"street": "1745 T Street Southeast",
"zip_code": 20020,
"city": "Louisville",
"destination_country": "USA",
"email": "test#test.com",
"articleNo": "B",
"articleImageUrl": "belt.jpg",
"quantity": 2,
"product_name": "Belt"
},
{
"orderNo": "2",
"tracking_number": "002",
"courier": "FedEx",
"street": "637 Britannia Drive",
"zip_code": 94591,
"city": "Vallejo",
"destination_country": "USA",
"email": "test#test.com",
"articleNo": "",
"articleImageUrl": "",
"quantity": "",
"product_name": ""
}
];
//tracking.json
const tracking = [{
"tracking_number": "001",
"location": "",
"timestamp": "2018-04-01T00:00:00.000Z",
"status": "OrderProcessed",
"status_text": "Order processed",
"status_details": "The order has been processed."
},
{
"tracking_number": "002",
"location": "",
"timestamp": "2018-04-06T05:58:00.000Z",
"status": "OrderProcessed",
"status_text": "Order processed",
"status_details": "The order has been processed."
}
]
//expected output data.json
const data = [
{
"orderNo":"1",
"tracking_number":"001",
"courier":"FedEx",
"street":"1745 T Street Southeast",
"zip_code":"20020",
"city":"Louisville",
"destination_country":"USA",
"email":"test#test.com",
"articleNo":"A",
"articleImageUrl":"watch.jpg",
"quantity":"1",
"product_name":"Watch",
"items":[
{
"articleNo":"A",
"articleImageUrl":"watch.jpg",
"quantity":"1",
"product_name":"Watch"
},
{
"articleNo":"B",
"articleImageUrl":"belt.jpg",
"quantity":"2",
"product_name":"Belt"
}
],
"tracking":{
"tracking_number":"001",
"location":null,
"timestamp":"2018-04-01T00:00:00.000Z",
"status":"Scheduled",
"status_text":"Order processed",
"status_details":"The order has been processed."
}
},
{
"orderNo": "2",
"tracking_number": "002",
"courier": "FedEx",
"street": "637 Britannia Drive",
"zip_code": 94591,
"city": "Vallejo",
"destination_country": "USA",
"email": "test#test.com",
"articleNo": "",
"articleImageUrl": "",
"quantity": "",
"product_name": "",
"items":[
],
"tracking":{
"tracking_number": "002",
"location": "",
"timestamp": "2018-04-06T05:58:00.000Z",
"status": "OrderProcessed",
"status_text": "Order processed",
"status_details": "The order has been processed."
}
}
]
const groupBy = key => array =>
array.reduce(
(objectsByKeyValue, obj) => ({
...objectsByKeyValue,
[obj[key]]: (objectsByKeyValue[obj[key]] || []).concat(obj)
}),
{}
);
const groupByOrders = groupBy('orderNo');
I am looking to get the data in the format shown in data.json
This is the current code along with the data
Code
Below is one possible way to achieve the target.
Code Snippet
const groupAndInclude = (base, delta) => {
const result = []; // result array
base.forEach( // iterate over the "orders"
({ // de-structure to directly access specific props
orderNo, tracking_number, articleNo,
articleImageUrl, quantity, product_name,
...rest // remaining props not directly-accessed
}) => {
const foundOrder = result.find( // find if "order" already in "result"
ob => ob.orderNo === orderNo
);
if (foundOrder) { // found a match, simply push "article"-info
if (articleNo.length > 0) { // push to "items" only if not "empty"
foundOrder.items.push({ // to the "items" array
articleNo, articleImageUrl, quantity, product_name
})
}
} else { // match not-found. Add new entry to "result"
const resObj = { // construct the result-object "resObj"
...rest, orderNo // using all relevant "order" props
};
if (articleNo.length > 0) { // do not add "empty" item
resObj.items = [{ // add "items" as an array
articleNo, articleImageUrl, quantity, product_name
}];
} else {
resObj.items = [];
};
const foundTracker = delta.find( // look for "tracking_number"
ob => ob.tracking_number === tracking_number
);
// if "tracking_number" found, add to result-object
if (foundTracker) resObj.tracking = {...foundTracker};
result.push(resObj); // push the result-object to the "result" array
}
}
);
return result; // explicitly return the "result" array
};
const orders = [{
"orderNo": "1",
"tracking_number": "001",
"courier": "FedEx",
"street": "1745 T Street Southeast",
"zip_code": 20020,
"city": "Louisville",
"destination_country": "USA",
"email": "test#test.com",
"articleNo": "A",
"articleImageUrl": "watch.jpg",
"quantity": 1,
"product_name": "Watch"
},
{
"orderNo": "1",
"tracking_number": "001",
"courier": "FedEx",
"street": "1745 T Street Southeast",
"zip_code": 20020,
"city": "Louisville",
"destination_country": "USA",
"email": "test#test.com",
"articleNo": "B",
"articleImageUrl": "belt.jpg",
"quantity": 2,
"product_name": "Belt"
},
{
"orderNo": "2",
"tracking_number": "002",
"courier": "FedEx",
"street": "637 Britannia Drive",
"zip_code": 94591,
"city": "Vallejo",
"destination_country": "USA",
"email": "test#test.com",
"articleNo": "",
"articleImageUrl": "",
"quantity": "",
"product_name": ""
}
];
//tracking.json
const tracking = [{
"tracking_number": "001",
"location": "",
"timestamp": "2018-04-01T00:00:00.000Z",
"status": "OrderProcessed",
"status_text": "Order processed",
"status_details": "The order has been processed."
},
{
"tracking_number": "002",
"location": "",
"timestamp": "2018-04-06T05:58:00.000Z",
"status": "OrderProcessed",
"status_text": "Order processed",
"status_details": "The order has been processed."
}
];
console.log(groupAndInclude(orders, tracking));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added in the snippet above.
You could take two objects as reference to same orderNo and tracking_number.
For adding an oder to the result set remove item properties and take a new object of this properties.
const
orders = [{ orderNo: "1", tracking_number: "001", courier: "FedEx", street: "1745 T Street Southeast", zip_code: 20020, city: "Louisville", destination_country: "USA", email: "test#test.com", articleNo: "A", articleImageUrl: "watch.jpg", quantity: 1, product_name: "Watch" }, { orderNo: "1", tracking_number: "001", courier: "FedEx", street: "1745 T Street Southeast", zip_code: 20020, city: "Louisville", destination_country: "USA", email: "test#test.com", articleNo: "B", articleImageUrl: "belt.jpg", quantity: 2, product_name: "Belt" }, { orderNo: "2", tracking_number: "002", courier: "FedEx", street: "637 Britannia Drive", zip_code: 94591, city: "Vallejo", destination_country: "USA", email: "test#test.com", articleNo: "", articleImageUrl: "", quantity: "", product_name: "" }],
tracking = [{ tracking_number: "001", location: "", timestamp: "2018-04-01T00:00:00.000Z", status: "OrderProcessed", status_text: "Order processed", status_details: "The order has been processed." }, { tracking_number: "002", location: "", timestamp: "2018-04-06T05:58:00.000Z", status: "OrderProcessed", status_text: "Order processed", status_details: "The order has been processed." }],
keys = ["articleNo", "articleImageUrl", "quantity", "product_name"],
items = {},
trackings = {},
result = [];
for (let order of orders) {
const item = {};
for (const key of keys) {
let value;
({ [key]: value, ...order } = order);
item[key] = value;
}
if (!items[order.orderNo]) result.push(items[order.orderNo] = { ...order, items: [] });
if (Object.values(item).some(Boolean)) items[order.orderNo].items.push(item);
trackings[order.tracking_number] = items[order.orderNo];
}
for (const transfer of tracking) trackings[transfer.tracking_number].tracking = transfer;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Reduce flat array to 2-level nested json object array

I have the following flat array:
{ "State": "New York", "Name": "Jane", "Product": "Apple" },
{ "State": "New York", "Name": "Jill", "Product": "Banana"},
{ "State": "California", "Name": "Jill", "Product": "Apple" },
{ "State": "California", "Name": "Jill", "Product": "Banana"}
Is it possible to create a 2-level nested array (i.e., Name > nested State Array > nested Product Array)? It would look like as follows:
{
"Name": "Jill",
"States": [
{
"State": "California",
"Products": [
{
"Product": "Apple"
},
{
"Product": "Banana"
}
]
},
{
"State": "New York",
"Products": [
{
"Product": "Banana"
}
]
}
]
},
{
"Name": "Jane",
"States": [
{
"State": "New York",
"Products": [
{
"Product": "Apple"
}
]
}
]
}
I have been able to get one level nested (States). How would you nest the second level?
Here is a stackblitz: https://stackblitz.com/edit/angular-lu6zj2
this.grouped_data = this.data.reduce((data, item) => {
data[item.Name] = data[item.Name] || { Name: item.Name, States: []}
data[item.Name].States.push(item)
return data;
}, {})
let data = [
{ "State": "New York", "Name": "Jane", "Product": "Apple" },
{ "State": "New York", "Name": "Jill", "Product": "Banana"},
{ "State": "California", "Name": "Jill", "Product": "Apple" },
{ "State": "California", "Name": "Jill", "Product": "Banana"}
];
let grouped = data.reduce((p, n) => {
// Create the Lady
if (!p[n.Name]) p[n.Name] = { States: [] };
// Check if the state exists, if not create it, then push product into it
if (!p[n.Name].States.some(state => state.State === n.State)) {
p[n.Name].States.push({ State: n.State, Products: [n.Product] });
} else {
!p[n.Name].States.find(state => state.State === n.State).Products.push(n.Product);
}
return p;
}, {});
console.log(grouped);
After that you can also remove duplicated products if you want to. I'll let you deal with it !
EDIT I didn't respect your model, what a dumbass am I ... Here it is :
let data = [
{ "State": "New York", "Name": "Jane", "Product": "Apple" },
{ "State": "New York", "Name": "Jill", "Product": "Banana"},
{ "State": "California", "Name": "Jill", "Product": "Apple" },
{ "State": "California", "Name": "Jill", "Product": "Banana"}
];
let grouped = data.reduce((p, n) => {
// Create the Lady
if (!p.some(lady => lady.Name === n.Name)) p.push({ Name: n.Name, States: [] });
let lady = p.find(lady => lady.Name === n.Name);
// Check if the state exists, if not create it, then push product into it
if (!lady.States.some(state => state.State === n.State)) {
lady.States.push({ State: n.State, Products: [n.Product] });
} else {
lady.States.find(state => state.State === n.State).Products.push(n.Product);
}
return p;
}, []);
console.log(grouped);

Nested .unionWith, with one unique merge per level

Original data looks like that:
let AddressesBook = [
{
"userName": "Jay12",
"doorNumber": "1512",
"cityID": 19,
"city": "London",
"countryID": 1,
"country": "UK",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "2003",
"cityID": 14,
"city": "York",
"countryID": 1,
"universe": "UK",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "435",
"cityID": 31,
"city": "Washington",
"countryID": 2,
"universe": "USA",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "1123",
"cityID": 18,
"city": "Oxford",
"countryID": 1,
"universe": "UK",
"houseType": "private"
}
];
i was mapping the data hierarchy by relevant unique ID using Lodash and
a suppurated dictionary:
function nestMaker(list, order) {
if (_.isEmpty(order)) return [];
let groups = _.groupBy(list, _.first(order));
return _.map(groups, (children, key) => {
let group = {};
group[_.first(order)] = key;
group.data = nestMaker(children, _.drop(order));
return _.isEmpty(group.data) ? _.omit(group, 'data') : group;
});
}
let hierarchical = nestMaker(AddressesBook, [
"countryID",
"cityID",
"houseType",
"doorNumber"]
);
it works fine, but i would like to have the name relevant to the id in each level of the object.
unfortunately you can't use _.groupBy on two keys. i was thinking about using _.unionWith separately from the first iteration but i couldn't find a way to use it recursively omitting the unnecessary data.
expected output:
let output =
[
{
"countryID": "1",
"country": "UK",
"data": [
{
"cityID": "14",
"city": "York",
"data": [
{
"houseType": "private",
"data": [
{
"doorNumber": "2003"
}
]
}
]
},
{
"cityID": "18",
"city": "Oxford",
"data": [
{
"houseType": "private",
"data": [
{
"doorNumber": "1123"
}
]
}
]
},
{
"cityID": "19",
"city": "London",
"data": [
{
"houseType": "private",
"data": [
{
"doorNumber": "1512"
}
]
}
]
}
]
},
{
"countryID": "2",
"country": "USA",
"data": [
{
"cityID": "31",
"city": "Washington",
"data": [
{
"houseType": "private",
"data": [
{
"doorNumber": "435"
}
]
}
]
}
]
}
];
You can get the 1st item in the group, and extract the name (country, city) from the item:
const AddressesBook = [{"userName":"Jay12","doorNumber":"1512","cityID":19,"city":"London","countryID":1,"country":"UK","houseType":"private"},{"userName":"Jay12","doorNumber":"2003","cityID":14,"city":"York","countryID":1,"country":"UK","houseType":"private"},{"userName":"Jay12","doorNumber":"435","cityID":31,"city":"Washington","countryID":2,"country":"USA","houseType":"private"},{"userName":"Jay12","doorNumber":"1123","cityID":18,"city":"Oxford","countryID":1,"country":"UK","houseType":"private"}];
const nestMaker = (list, order) => {
if (_.isEmpty(order)) return [];
const idKey = _.first(order);
const nameKey = idKey.replace('ID', '');
let groups = _.groupBy(list, idKey);
return _.map(groups, (children, key) => {
const group = {};
const child = _.first(children);
group[idKey] = key;
if(_.has(child, nameKey)) group[nameKey] = child[nameKey];
group.data = nestMaker(children, _.drop(order));
return _.isEmpty(group.data) ? _.omit(group, 'data') : group;
});
}
const hierarchical = nestMaker(AddressesBook, [
"countryID",
"cityID",
"houseType",
"doorNumber"
]);
console.log(hierarchical);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
If the id and the name keys are doesn't follow the same pattern, you can explicitly state them as entry in the order:
const AddressesBook = [{"userName":"Jay12","doorNumber":"1512","cityID":19,"city":"London","countryID":1,"universe":"UK","houseType":"private"},{"userName":"Jay12","doorNumber":"2003","cityID":14,"city":"York","countryID":1,"universe":"UK","houseType":"private"},{"userName":"Jay12","doorNumber":"435","cityID":31,"city":"Washington","countryID":2,"universe":"USA","houseType":"private"},{"userName":"Jay12","doorNumber":"1123","cityID":18,"city":"Oxford","countryID":1,"universe":"UK","houseType":"private"}];
const nestMaker = (list, order) => {
if (_.isEmpty(order)) return [];
const entry = _.first(order);
const [idKey, nameKey] = Array.isArray(entry) ? entry : [entry];
let groups = _.groupBy(list, idKey);
return _.map(groups, (children, key) => {
const group = {};
const child = _.first(children);
group[idKey] = key;
if(_.has(child, nameKey)) group[nameKey] = child[nameKey];
group.data = nestMaker(children, _.drop(order));
return _.isEmpty(group.data) ? _.omit(group, 'data') : group;
});
}
const hierarchical = nestMaker(AddressesBook, [
["countryID", "universe"],
["cityID", "city"],
"houseType",
"doorNumber"
]);
console.log(hierarchical);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
This is a bit manual but does the job.
let AddressesBook = [{
"userName": "Jay12",
"doorNumber": "1512",
"cityID": 19,
"city": "London",
"countryID": 1,
"country": "UK",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "2003",
"cityID": 14,
"city": "York",
"countryID": 1,
"country": "UK",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "435",
"cityID": 31,
"city": "Washington",
"countryID": 2,
"country": "USA",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "1123",
"cityID": 18,
"city": "Oxford",
"countryID": 1,
"country": "UK",
"houseType": "private"
}
];
database = []
AddressesBook.forEach(a => {
doesExist = database.some(d => (d.countryID == a.countryID))
if (doesExist) {
let instance = database.filter(d => d.countryID == a.countryID)[0]
instance.data.push({
"cityID": a.cityID,
"city": a.city,
"data": [{
"houseType": a.houseType,
"data": [{
"doorNumber": a.doorNumber
}]
}]
})
} else {
database.push({
"countryID": a.countryID,
"country": a.country,
"data": [{
"cityID": a.cityID,
"city": a.city,
"data": [{
"houseType": a.houseType,
"data": [{
"doorNumber": a.doorNumber
}]
}]
}]
})
}
})
console.log(database)

Sort nested json complex array

I search how to sort by place.city this kind of object who have id's for keys. The need is to keep id's for first keys…
{
"123": {
"place": {
"city": "New York",
"country": "USA"
},
"person": {
"name": "Bob",
"age": 45
}
},
"456": {
"place": {
"city": "Chicago",
"country": "USA"
},
"person": {
"name": "Louis",
"age": 34
}
},
"789": {
"place": {
"city": "Dallas",
"country": "USA"
},
"person": {
"name": "Kevin",
"age": 27
}
}
}
I try some kind of function like this and the expected result is not here.
let result = _(myObject).map(function (value, key) {
return _.defaults({ name: key }, value)
}).sortBy('city').value()
You can't sort an object.. You can, however, convert your object to an array and sort that.
var data ={
"123" : {
"place": {
"city": "New York",
"country": "USA"
},
"person": {
"name": "Bob",
"age": 45
}
},
"456" : {
"place": {
"city": "Chicago",
"country": "USA"
},
"person": {
"name": "Louis",
"age": 34
}
},
"789" : {
"place": {
"city": "Dallas",
"country": "USA"
},
"person": {
"name": "Kevin",
"age": 27
}
}
};
var sortedByPlace = _.sortBy(Object.keys(data).map(k => ({id:k, ...data[k]})), (d)=> d.place.city)
console.log(sortedByPlace);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.5/lodash.min.js"></script>
It is not possible to sort an object, you need to make it a list.
import { map, flow } from 'lodash'
import { sortBy } from 'lodash/fp'
cities => flow(
map(places, (place, id) => { id, ...place }),
sortBy('city'),
)()
Your second question begs the question (mh...) if you want local sort. That would be
import { mapValues } from 'lodash'
import { sortBy } from 'lodash/fp'
data => mapValues(data, sortBy('place.city'))
You cant sort an object. However you could create a sorted array that contains references to the objects:
const sorted = Object.values(myObject).sort((a, b) => a.place.city.localeCompare(b.place.city));
If you look at this answer, the following should work
var sort = function (prop, arr) {
prop = prop.split('.');
var len = prop.length;
arr.sort(function (a, b) {
var i = 0;
while( i < len ) { a = a[prop[i]]; b = b[prop[i]]; i++; }
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
return arr;
};
sort("place.city", myObject);
I realize the object I have to treat (get from an obscur API) is a little bit more complicated than my first example :/
So the responses you nicely give are not working anymore…
Do you see the subtlety on this new object ?
The point stay to sort by place.city
{
"123": {
0: {
"place": {
"city": "New York",
"country": "USA"
},
"person": {
"name": "Bob",
"age": 45
}
},
1: {
"place": {
"city": "New York",
"country": "USA"
},
"person": {
"name": "James",
"age": 32
}
}
},
"456": {
0: {
"place": {
"city": "Chicago",
"country": "USA"
},
"person": {
"name": "Louis",
"age": 34
}
},
1: {
"place": {
"city": "Chicago",
"country": "USA"
},
"person": {
"name": "Christine",
"age": 65
}
}
},
"789": {
0: {
"place": {
"city": "Dallas",
"country": "USA"
},
"person": {
"name": "Kevin",
"age": 27
}
},
1: {
"place": {
"city": "Dallas",
"country": "USA"
},
"person": {
"name": "Robert",
"age": 55
}
},
2: {
"place": {
"city": "Dallas",
"country": "USA"
},
"person": {
"name": "Danny",
"age": 62
}
}
}
}

Nested Array in JSON Objects Conversion

I'm struggling with converting the nested JSON array that I have.
{
"Id": "1234",
"Company": {
"element": [{
"Name": "htc",
"Contacts": {
"element": [{
"name": "john",
"phone": "1234"
}, {
"name": "peter",
"phone": "5678"
}]
},
"Address": {
"element": {
"country": "us",
"state": "cali"
}
}
}, {
"Name": "samsung",
"Contacts": {
"element": [{
"name": "luke",
"phone": "0011"
}, {
"name": "james",
"phone": "2233"
}]
},
"Address": {
"element": {
"country": "us",
"state": "texas"
}
}
}]
}
}
As you'll notice, there's this "element" in the arrays "Company", "Contacts" and "Address". But the output that I need to provide should not contain the "element" such as this code:
{
"Id": "1234",
"Company": [{
"Name": "htc",
"Contacts": [{
"name": "john",
"phone": "1234"
}, {
"name": "peter",
"phone": "5678"
}],
"Address": [{
"country": "us",
"state": "cali"
}]
}, {
"Name": "samsung",
"Contacts": [{
"name": "luke",
"phone": "0011"
}, {
"name": "james",
"phone": "2233"
}],
"Address": [{
"country": "us",
"state": "texas"
}]
}]
}
I have no clue how to do in JavaScript. Any ideas/tips are appreciate.
Thank you
You can try something like this:
var data={Id:"1234",Company:{element:[{Name:"htc",Contacts:{element:[{name:"john",phone:"1234"},{name:"peter",phone:"5678"}]},Address:{element:{country:"us",state:"cali"}}},{Name:"samsung",Contacts:{element:[{name:"luke",phone:"0011"},{name:"james",phone:"2233"}]},Address:{element:{country:"us",state:"texas"}}}]}};
var keysToClean = ["Address", "Contacts"]
// Copy object instead of reference
var result = Object.assign({}, data);
result.Company = result.Company.element;
result.Company.forEach(x => {
keysToClean.forEach(k => {
x[k] = Array.isArray(x[k]) ? x[k].element : [x[k].element]
})
})
console.log(result);
Note: I have use Object.create and Arrow functions. They are not supported by old browsers. You can refer to following link for alternative to deep copy an object:
What is the most efficient way to deep clone an object in JavaScript?
The solution using Array.prototype.forEach() function:
var companyData = { "Id": "1234", "Company": { "element": [{ "Name": "htc", "Contacts": { "element": [{ "name": "john", "phone": "1234" }, { "name": "peter", "phone": "5678" }] }, "Address": { "element": { "country": "us", "state": "cali" } } }, { "Name": "samsung", "Contacts": { "element": [{ "name": "luke", "phone": "0011" }, { "name": "james", "phone": "2233" }] }, "Address": { "element": { "country": "us", "state": "texas" } } }] }
};
companyData.Company = companyData.Company.element;
var omitElement = function(o){
if (!o['element']) return o;
return (Array.isArray(o.element))? o.element : [o.element];
}
companyData.Company.forEach(function (o) {
o.Contacts = omitElement(o.Contacts);
o.Address = omitElement(o.Address);
});
console.log(companyData);
Please see this Plunker This should help.. it will generate desired result you need but be aware this is just a way to do this, and only meant for information purpose. It's not production grade...
function ParseData(data)
{
var newObject={Id:0, Company:[]};
newObject["Id"]=data["Id"];
newObject["Company"]=CreateCompanyObject(data["Company"]["element"]);
console.log(JSON.stringify(newObject));
}
function CreateCompanyObject(data)
{
var companies=[];
for(var i=0;i<data.length;i++)
{
companies.push({
name:data[i].Name,
contacts:CreateContactObject(data[i].Contacts.element),
Address:CreateAddressObject(data[i].Address.element)});
};
return companies;
}
function CreateContactObject(data){
var contacts=[];
for(var i=0;i<data.length;i++)
contacts.push(data[i]);
return contacts;
}
function CreateAddressObject(data){
var address=[];
if(typeof(data)=="array"){
for(var i=0;i<data.length;i++)
address.push(data[i]);
}
else
address.push(data);
return address;
}
You could check for element and move the content a step ahead to its parent.
function deleteElement(object){
Object.keys(object).forEach(function (k) {
if (object[k] && typeof object[k] === 'object') {
if ('element' in object[k]) {
object[k] = Array.isArray(object[k].element) ?
object[k].element :
[object[k].element];
}
deleteElement(object[k]);
}
});
}
var data = { Id: "1234", Company: { element: [{ Name: "htc", Contacts: { element: [{ name: "john", phone: "1234" }, { name: "peter", phone: "5678" }] }, Address: { element: { country: "us", state: "cali" } } }, { Name: "samsung", Contacts: { element: [{ name: "luke", phone: "0011" }, { name: "james", phone: "2233" }] }, Address: { element: { country: "us", state: "texas" } } }] } };
deleteElement(data);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources