Converting an array of data to JSON using a property map - javascript

I am trying to convert a 2d array into a json object using a key map.
The key map looks like
var keys = ['id', 'title', 'customer.id', 'customer.name', 'customer.phone.home', 'customer.phone.mobile' ];
and the data is
var data = [
[1, 'Task 1', 'C1', 'Customer 1', '999', '8888'],
[2, 'Task 2', 'C2', 'Customer 2', '333', '5555']
];
Output JSON should be
var output = [
{
"id":1,
"title":"Task 1",
"customer":{
"id":"C1",
"name":"Customer 1",
"phone":{
"home":"999",
"mobile":"8888"
}
}
},
{
"id":2,
"title":"Task 2",
"customer":{
"id":"C2",
"name":"Customer 2",
"phone":{
"home":"333",
"mobile":"5555"
}
}
}
];
I am trying to do it something like but I am not good here making smerecursion etc. Could anyone help please?
function arrToJSON(headers, data){
var output = [];
data.forEach(row, index){
var cObj = {};
headers.forEach(header, itemIndex){
var headerParts = header.split('.');
// NOt sure what to do here
}
}
}

You can easily achieve the result using map and reduce in js.
createObj(acc, curr.split("."), 0, o[index]);
is the function that is used in recursion and that is what you're looking for.
Arguments
createObj(
acc, // object in which you want to add value
curr.split("."), // send path as an array
0, // current index in path, initially zero
o[index] // value to be assigned
);
var keys = [
"id",
"title",
"customer.id",
"customer.name",
"customer.phone.home",
"customer.phone.mobile",
];
var data = [
[1, "Task 1", "C1", "Customer 1", "999", "8888"],
[2, "Task 2", "C2", "Customer 2", "333", "5555"],
];
function createObj(obj, arr, index, value) {
if (index === arr.length - 1) obj[arr[index]] = value;
else {
if (!obj[arr[index]]) obj[arr[index]] = {};
createObj(obj[arr[index]], arr, index + 1, value);
}
}
const result = data.map((o) => {
return keys.reduce((acc, curr, index) => {
createObj(acc, curr.split("."), 0, o[index]);
return acc;
}, {});
});
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}

You can use simply use destructure and spread operator with reduce.
var data = [
[1, "Task 1", "C1", "Customer 1", "999", "8888"],
[2, "Task 2", "C2", "Customer 2", "333", "5555"],
];
const buildObject = (arr = []) => {
return arr.reduce((acc, [id, title, cid, name, home, mobile]) => {
const row = {
id,
title,
customer: { id: cid, name, phone: { home, mobile } },
};
return acc.concat(row);
}, []);
};
console.log(buildObject(data));

Related

array of arrays conversion in Javascript

I need to create an array of array.
It is worth noting that the database is very large and that if any attribute does not have a corresponding value, it sends an empty string. I've tried with map and reduce but I wasn't successful:
Any help will be appreciated.
Below I show an example of the expected output:
outputExpected = [
["id", 1, 2],
["name", "name1", "name2"],
["price", 6.95, 998.95],
["promoPrice", 5.91, 333.91],
["category", "test1 | test2", "test3 | test4"],
]
Any way to solve this problem performatically?
this is my code:
let arrayObj = [{
"id": 1,
"name": "name1",
"price": 6.95,
"promoPrice": 5.91,
"category": ["test1, test2"]
},
{
"id": 2,
"name": "name2",
"price": 998.95,
"promoPrice": 333.91,
"category": ["test3, test4"]
}
]
const headers = ["id", "name", "price", "promoPrice", "category"]
const result1 = headers.concat(arrayObj.map((obj) => {
return headers.reduce((arr, key) => {
arr.push(obj[key]) return arr;
}, [])
}))
console.log(result1)
Reduce the array to a Map. On each iteration convert the object to an array of [key, value] pairs using Object.entries(). Use Array.forEach() to iterate the entries and add them to the map. Convert the Map's values iterator to an array using Array.from():
const arr = [{"id":1,"name":"name1","price":6.95,"promoPrice":5.91,"category":["test1", "test2"]},{"id":2,"name":"name2","price":998.95,"promoPrice":333.91,"category":["test3", "test4"]}]
const result = Array.from(arr.reduce((acc, o) => {
Object.entries(o)
.forEach(([k, v]) => {
if(!acc.has(k)) acc.set(k, [k])
acc.get(k).push(Array.isArray(v) ? v.join(' | ') : v)
})
return acc
}, new Map()).values())
console.log(result)
You could simply map the value and check if an item is an array, then take the joined values or the value itself.
const
data = [{ id: 1, name: "name1", price: 6.95, promoPrice: 5.91, category: ["test1, test2"] }, { id: 2, name: "name2", price: 998.95, promoPrice: 333.91, category: ["test3, test4"] }],
headers = ["id", "name", "price", "promoPrice", "category"],
result = data
.reduce(
(r, o) => headers.map((k, i) => [
...r[i],
Array.isArray(o[k]) ? o[k].join(' | ') : o[k]
]),
headers.map(k => [k]),
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
construct one init array with all possible keys with the wanted order, then uses Array.reduce and Array.forEach to Array.push value for per key based on its index.
const arrayObj = [
{
"id":1,
"name":"name1",
"price":6.95,
"promoPrice":5.91,
"category":["test1", "test2"]
},
{
"id":2,
"name":"name2",
"price":998.95,
"promoPrice":333.91,
"category":["test3", "test4"]
}
]
function ConvertToArray2D (items) {
let init = [['id'], ['name'], ['price'], ['promoPrice'], ['category']]
if (!items) return init
return arrayObj.reduce((pre, cur) => {
init.forEach((key, index) => {
pre[index].push(Array.isArray(cur[key[0]]) ? cur[key[0]].join('|') : cur[key[0]])
})
return pre
}, init.slice())
}
console.log(ConvertToArray2D(arrayObj))
This can be handled with a standard 'zip' after mapping your objects to arrays of values in line with the headers array. (This also allows for the result to be pivoted back).
//** #see https://stackoverflow.com/a/10284006/13762301
const zip = (...rs) => [...rs[0]].map((_, c) => rs.map((r) => r[c]));
const headers = ['id', 'name', 'price', 'promoPrice', 'category'];
const arrayObj = [{ id: 1, name: 'name1', price: 6.95, promoPrice: 5.91, category: ['test1', 'test2'] },{ id: 2, name: 'name2', price: 998.95, promoPrice: 333.91, category: ['test3', 'test4'] },];
const result = zip(
headers,
...arrayObj.map((o) => headers.map(h => Array.isArray(o[h]) ? o[h].join(' | ') : o[h]))
);
console.log(result);
// which also allows it to be reversed
console.log(zip(...result));
.as-console-wrapper { max-height: 100% !important; top: 0; }
see: Javascript equivalent of Python's zip function for further zip discussion.

how to add objects to arrays

I'm trying to construct an array of objects from a set of two different arrays. I'm a little comfussed on where I need to go from here.
I'm creating a unique key value, but the object is going into the array individual.
const startingArray = [
{
key: "description",
value: "my description"
},
{
key: "description2",
value: "my description2"
},
{
key: "description3",
value: "my description3"
},
]
my logic
const k = mystartingArray.reduce((acc, d, count) => {
const name = Object.value(d)[0]
const title = Object.value(d)[1]
const value = {
[name]: title
}
acc.push(value)
return acc
},[])
how I want the Array to look
const finishedArray = [
{
description: "my description",
description2: "my description2,
description3: "my description3,
}
How far am I off?
I think this would be simpler to solve just by using a basic forEach.
let value = {};
startingArray.forEach(obj => {
value[obj.key] = obj.value;
});
const finishedArray = [value];
Or, if you don't want to have a value object:
const finishedArray = [{}];
startingArray.forEach(obj => {
finishedArray[0][obj.key] = obj.value;
});
const finishedArray = [
startingArray.reduce((a, v) => {a[v.key] = v.value; return a}, {})
]
To finish your code:
const startingArray = [
{
key: "description",
value: "my description"
},
{
key: "description2",
value: "my description2"
},
{
key: "description3",
value: "my description3"
},
];
const k = startingArray.reduce((acc, d, count) => {
return [{
...(acc[0] || {}),
[d.key]: d.value
}]
},[])
console.log(k);
However, I think the solution of Rocket Hazmat is more reasonable than this.

How to rename key inside a nested array?

Good day SO. I have seen other SO questions on how to rename a key in an array but not on a nested array.
With this array = [{id: 1, data: "Data1"}, {id: 2, data: "Data2"}] array as an example, I can change the name data to text with this code snippet:
array = array_list.map(
({ id, data, }) => ({
id: id,
text: data,
})
);
However, I dont know how to rename my key in a nested array like this:
array = ([
{
"id": 1,
"text": "Data1",
"children": [{ "minor_id": 1, "minor_data": "minor data 1" },
{ "minor_id": 2, "minor_data": "minor data 2" }]
}
])
I want to rename minor_id to id and minor_data to text so that I can use this for a select2
Please help.
Basically, this is my desired output:
SO Select2 Sample
Please find the required method below:-
function modify(array) {
return array.map((item) => {
const { children } = item
if (children.length) {
return {
...item,
children: children.map(({ minor_id, minor_data }) => ({
id: minor_id,
text: minor_data
}))
}
}
return { ...item }
})
}
Use a nested map() for the children
const arr = ([
{
"id": 1,
"text": "Data1",
"children": [{ "minor_id": 1, "minor_data": "minor data 1" },
{ "minor_id": 2, "minor_data": "minor data 2" }]
}
])
const res = arr.map(({children, ...other}) => {
children = children.map(({minor_id:id , minor_data:text}) => ({id, text}))
return {...other, children };
})
console.log(res)
This is your given array
let array = ([
{
"id": 1,
"text": "Data1",
"children": [{ "minor_id": 1, "minor_data": "minor data 1" },
{ "minor_id": 2, "minor_data": "minor data 2" }]
}
]);
you can parse it in this way
for (let i = 0; i < array.length; i++) {
array[i].children.map(({minor_id, minor_data}) => ({id: minor_id, text: minor_data}));
}

Parse JSON object for unique records

I have following JSON object:
var data =JSON.parse('[{"Title":"Test 1","State":"Colorado"},
{"Title":"Test 1","State":"Arizona"},
{"Title":"Test 2","State":"Utah"},
{"Title":"Test 2","State":"Arizona"},
{"Title":"Test 3","State":"Arizona"}]');
How do I parse this data object so that I get following output:
resultData = [{"Title":"Test 1", State: ["Colorado", "Arizona"]},
{"Title":"Test 2", State: ["Utah", "Arizona"]},
{"Title":"Test 3", State: ["Arizona"]}]
So far I tried following:
var resultData = {},
groupBy = "Title";
for (var i = 0; i < data.length; i++) {
if (!resultData[data[i][groupBy]])
resultData[data[groupBy]] = [];
resultData[data[i][groupBy]].push(data[i]);
};
but it didn't help and returning some odd object as below:
resultData = [{Test 1: [{State: "Colorado"}, {State: "Arizona"}]},
{Test 2: [{State: "Utah"}, {State: "Arizona"}]},
{Test 3: [{State: "Arizona"}]}]
Can someone help me achieve the same.
what are you trying to achieve is a perfect case for reduce:
Step 1 - parse data as you already did
var data =JSON.parse('[{"Title":"Test 1","State":"Colorado"},
{"Title":"Test 1","State":"Arizona"},
{"Title":"Test 2","State":"Utah"},
{"Title":"Test 2","State":"Arizona"},
{"Title":"Test 3","State":"Arizona"}]');
Step 2 - combine states with same title into array
var titles = data.reduce(function(acc, item){
var title = item.Title;
var state = item.State;
if (!Object.prototype.hasOwnProperty.call(acc, title)){
acc[title] = [];
}
acc[title].push(state);
return acc;
}, {});
Step 3 - build final array using combined states
var resultData = Object.keys(titles).map(function(title){
return {
Title: title,
State: titles[title]
}
});
var data = JSON.parse('[{"Title":"Test 1","State":"Colorado"}, {"Title":"Test 1","State":"Arizona"},{"Title":"Test 2","State":"Utah"},{"Title":"Test 2","State":"Arizona"},{"Title":"Test 3","State":"Arizona"}]');
var titles = data.reduce(function(acc, item) {
var title = item.Title;
var state = item.State;
if (!Object.prototype.hasOwnProperty.call(acc, title)) {
acc[title] = [];
}
acc[title].push(state);
return acc;
}, {});
var resultData = Object.keys(titles).map(function(title) {
return {
Title: title,
State: titles[title]
}
});
console.log(resultData)
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use a single loop approach with a closure over a hash table for the same groups.
var data = [{ Title: "Test 1", State: "Colorado" }, { Title: "Test 1", State: "Arizona" }, { Title: "Test 2", State: "Utah" }, { Title: "Test 2", State: "Arizona" }, { Title: "Test 3", State: "Arizona" }],
key = 'Title',
grouped = data.reduce(function (group) {
return function (r, o) {
if (!group[o[key]]) {
group[o[key]] = [];
r.push({ Title: o.Title, State: group[o[key]] });
}
group[o[key]].push(o.State);
return r;
};
}(Object.create(null)), []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

JavaScript Get values from array but ignore duplicates

I have an array that contains different clothes and the type of the cloth. For example, I may have a specific shirt that belongs to the shirts category. What I want to do is get all the types from an array and ignore any duplicate entries. So if I have 3 shirts and 2 trousers, I will only get 1 shirt and 1 trouser.
array = [
{
name: "test 1",
type: "shirt"
},
{
name: "test 2",
type: "shirt"
},
{
name: "test 3",
type: "trousers"
},
{
name: "test 4",
type: "trousers"
}
];
var categories = {};
for(var i = 0, len = array.length; i < len; i++) {
if(categories.indexOf(array[i].type) > -1) {
console.log('Duplicate type');
}
else {
console.log('New type');
categories.push(array[i].type);
}
}
But I end up getting TypeError: categories.indexOf is not a function.
Pretty short solution using ES6 Set object:
The Set object lets you store unique values of any type, whether
primitive values or object references.
var categories = new Set();
array.forEach((o) => categories.add(o.type));
categories = [...categories]; // Set to Array transformation
console.log(categories); // ["shirt", "trousers"]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
You need an array for categories, not an object.
var categories = [];
array = [
{
name: "test 1",
type: "shirt"
},
{
name: "test 2",
type: "shirt"
},
{
name: "test 3",
type: "trousers"
},
{
name: "test 4",
type: "trousers"
}
];
var categories = [];
for(var i = 0, len = array.length; i < len; i++) {
if(categories.indexOf(array[i].type) > -1) {
console.log('Duplicate type');
}
else {
console.log('New type');
categories.push(array[i].type);
}
}
console.log(categories);
This happens because you define categories as object literal ({}), rather than an array ([]):
// --------------vv
var categories = {};
Your issue is that you are trying to invoke .push method on an object but the method is available only on Array. You need to make categories an array in order to push to it.
As an alternative, you can use pure function without any mutations using Array.prototype.reduce() to reduce the array of duplicate objects to unique ones:
var array = [
{
name: "test 1",
type: "shirt"
},
{
name: "test 2",
type: "shirt"
},
{
name: "test 3",
type: "trousers"
},
{
name: "test 4",
type: "trousers"
}
];
function unique(input) {
return input.reduce(function (acc, current) {
if (acc.indexOf(current.type) > -1) {
return acc
} else {
return acc.concat([current.type]);
}
}, [])
}
var categories = unique(array);
console.log(categories);
If you want to see the result of every row then I think first implementation could be the answer but if you want just the categories then using map make it simple.
array = [
{ name: "test 1", type: "shirt" },
{ name: "test 2", type: "shirt" },
{ name: "test 3", type: "trousers" },
{ name: "test 4", type: "trousers" }
];
// --------------------------------------
var categories = [];
array.forEach(v => {
if (this[v.type])
return console.log('Duplicate type');
console.log('New type');
this[v.type] = true;
categories.push(v.type);
}, {});
console.log(categories);
// --------------------------------------
var map = new Map;
array.forEach(v => map.set(v.type, v.type));
categories = [...map.keys()];
console.log(categories);

Categories