Convert array object into object in Javascript - javascript

I am trying to covert an array object into object array with matching filterKey and filterValue of current object.
[
{
"filterKey": "name",
"filterValue": [
{
"value": "abc"
},
{
"value": "def"
}
]
},
{
"filterKey": "status",
"filterValue": [
{
"value": "active"
},
{
"value": "pending"
}
]
}
]
The result I am expecting as below:
{
"name": [
"abc",
"def"
],
"statuses": [
"active",
"pending"
]
}
I tried
obj.map(item => Object.values(item.filterValue))

reduce over the array of data, and create a new object, using map to create a new array of values for each new property.
const data=[{filterKey:'name',filterValue:[{value:'abc'},{value:'def'}]},{filterKey:'status',filterValue:[{value:'active'},{value:'pending'}]}];
// Iterate over the array with `reduce`
const out = data.reduce((acc, obj) => {
// Extract the key and value from each object
const { filterKey: key, filterValue: value } = obj;
// Return the accumulator object updated with the new key
// and array of values gained from `map`
return {...acc, [key]: value.map(o => o.value)};
}, {});
console.log(out);

const oldArr = [{"filterKey": "name","filterValue": [{"value": "abc"},{"value": "def"}]},{"filterKey": "status","filterValue": [{"value": "active"},{"value": "pending"}]}];
const newObj = oldArr.reduce((a, b) => { // reduce to single object
const values = b.filterValue.map(x => x.value); // map the filter values to their strings
if (a[b.filterKey]) a[b.filterKey].concat(values); // if the key already exists add the strings to it (does not apply to your example)
else a[b.filterKey] = values; // else create a new entry with the strings
return a;
}, {});
// if you really want to rename status => statuses
newObj.statuses = newObj.status;
delete newObj.status;
console.log(newObj);

Related

How to convert an array with one object and multiple keys into an array of multiple objects using those keys and their values?

I have an array like so with a single object inside:
FirstArray = [{
"category": "None",
"ARFE": 553.5,
"BV": 900,
"RF rfeer": 0,
.....
}]
I want to convert it so that every key-value pair (where the value is a number) in the object is in its own object like the following:
NewArray = [{
name: "ARFE",
value: 553.05
}, {
name: "BV",
value: 900
}, {
name: "RF rfeer",
value: 0
}, .....]
Here, each key was assigned a new key called name, and the value for the original key was assigned a new key called value. Those pairs are then put into their own object inside the array.
Note that "category": "None" is not its own object in the array since "None" is non-numerical.
It's also important to note that there could be many key-value pairs, so it's not just limited to the items above (e.g., "ARFE": 553.5, etc.)
What I have so far:
I know you can separate a single object into multiple objects:
NewArray = Object.entries(FirstArray).reduce((prev, [og, nw]) => {
let [name, value] = og.match(/\D+|\d+$/g)
prev[value] = { ...(prev[value] || {}), [name]: nw }
return prev;
}, {})
I also know how that you can create a new object with new keys like so:
NewArray = Object.assign(
...Object.entries(FirstArray).map(([key, value]) => ({ [key]: name }))
);
However, I'm having trouble putting everything together. How would I be able to achieve NewArray from FirstArray?
You were pretty close. All you needed to do is specify the name:
const data = {
"category": "None",
"ARFE": 553.5,
"BV": 900,
"RF rfeer": 0
};
const result = Object
.entries(data)
.filter(([_, value]) => typeof value === 'number')
.map(([key, value]) => ({ name: key, value }));
console.log(result);
Also, if you don't want { "name": "category", "value": "None" } to be included in the result, you can just filter it:
const result = Object
.entries(data)
.filter(([ key ]) => key !== 'category')
.map(([key, value]) => ({ name: key, value }));
Object.entries on array has no sense at all, use it on the object
const FirstArray = [{
"category": "None",
"ARFE": 553.5,
"BV": 900,
"RF rfeer": 0,
}]
const newObject = Object.entries(FirstArray[0]).reduce((array, [key, value]) => {
return [...array, {
name: key,
value
}]
}, [])
console.log(newObject)
reduce is not the right way to go. Simply use map:
Object.entries(FirstArray[0])
.filter(x => !isNaN(x[1])) // filter out non-numeric values
.map(([name, value]) => ({name, value}))

Grouping elements of array on the basis of property

I have a array as follows:
data = [
{
"id":1
"name":"london"
},
{
"id":2
"name":"paris"
},
{
"id":3
"name":"london"
},
{
"id":4
"name":"paris"
},
{
"id":5
"name":"australia"
},
{
"id":6
"name":"newzearland"
}
]
At runtime this array can have n number of elements. I want to group this array with respect to name attribute. All the elements with same name should be moved to a separate array. I don't know the what value can name have in advance. This is coming at runtime. For example, from above array I want final output as follows:
output:
newArray1 = [
{
"id":1
"name":"london"
},
{
"id":3
"name":"london"
}
]
newArray2 = [
{
"id":2
"name":"paris"
},
{
"id":4
"name":"paris"
}
]
newArray3 = [
{
"id":5
"name":"australia"
}
]
newArray4 = [
{
"id":6
"name":"newzearland"
}
]
How can I do that?
As Teemu has already pointed out in a comment, creating new variables to store the data is not ideal. You would have no way of knowing how many groups you've created and using variables that you can't be sure exist is not the best way to write code. Fortunately, JavaScript has objects, which can store data like this in a much cleaner way. Here's the code I've come up with:
function groupBy(arr, key) {
let res = {}
for (let element of arr) {
if (res.hasOwnProperty(element[key])) {
res[element[key]].push(element)
} else {
res[element[key]] = [element]
}
}
return res
}
This code is not the best, most efficient code ever, but it is written to be easier to understand for someone still learning. This code loops over every element in your data and checks whether our result already contains an array for elements with that name. If there's already an array for elements with that name, the current element is added to it. If there isn't one, a new one is created with the current element inside it. To do exactly what you want, you'd call this function with groupBy(data, "name") and assign it to a new variable like groupedData (THIS DOES NOT MODIFY THE DATA, IT RETURNS A NEW OBJECT OF GROUPED DATA) .
Start by getting all the unique .names, then map them to the original array filtered by each .name:
const data = [{
"id": 1, "name": "london"
},
{
"id": 2, "name": "paris"
},
{
"id": 3, "name": "london"
},
{
"id": 4, "name": "paris"
},
{
"id": 5, "name": "australia"
},
{
"id": 6, "name": "newzearland"
}
];
const newData = [...new Set(data
//Get all names in an array
.map(({name}) => name))]
//For each name filter original array by name
.map(n => data.filter(({name}) => n === name));
console.log( newData );
//OUTPUT: [newArray1, newArray2, .....]
You can get the expected result with grouping by key approach.
const data = [{"id":1,"name":"london"},{"id":2,"name":"paris"},{"id":3,"name":"london"},{"id":4,"name":"paris"},{"id":5,"name":"australia"},{"id":6,"name":"newzearland"}];
const result = Object.values(data.reduce((acc, obj) =>
({ ...acc, [obj.name]: [...(acc[obj.name] ?? []), obj] }), {}));
console.log(result);
const [newArray1, newArray2, newArray3, newArray4, ...rest] = result;
console.log('newArray1:', newArray1);
console.log('newArray2:', newArray2);
console.log('newArray3:', newArray3);
console.log('newArray4:', newArray4);
.as-console-wrapper{min-height: 100%!important; top: 0}

Transform an array of objects by removing object properties not contained in another array

A long title, so I´ll explain the problem by example. I have an array of objects:
const myObjects = [
{
id: 1,
name: "a",
stuff: "x"
},
{
id: 2,
name: "b",
stuff: "y"
},
];
Then I have another array of objects like this:
const myTemplate=[
{
desiredProperty: "name",
someOtherProperty: "..."
},
{
desiredProperty: "stuff",
someOtherProperty: "..."
},
];
Now I want to transform myObjects array to new one, so that the individual objects contain only the properties listed in desiredProperty of each object in myTemplate.
The result should look like this:
myResult = [
{
name: "a",
stuff: "x"
},
{
name: "b",
stuff: "y"
}
]
How to achieve this?
This approach lets you partially apply the template to get back a reusable function to run against multiple sets of inputs:
const convert = (template, keys = new Set (template .map (t => t .desiredProperty))) => (xs) =>
xs .map (
(x) => Object .fromEntries (Object .entries (x) .filter (([k, v]) => keys .has (k)))
)
const myObjects = [{id: 1, name: "a", stuff: "x"}, {id: 2, name: "b", stuff: "y"}]
const myTemplate= [{desiredProperty: "name", someOtherProperty: "..."}, {desiredProperty: "stuff", someOtherProperty: "..."}]
console .log (
convert (myTemplate) (myObjects)
)
But I agree with the comment that the template here is better expressed as an array of keys to keep.
The following code creates a Set of the keys you want to keep. Then, we map over your myObjects array and only keep the object keys that are in the toKeep Set.
const myObjects=[{id:1,name:"a",stuff:"x"},{id:2,name:"b",stuff:"y"}];
const myTemplate=[{desiredProperty:"name",someOtherProperty:"..."},{desiredProperty:"stuff",someOtherProperty:"..."}];
const toKeep = new Set(myTemplate.map(t => t.desiredProperty));
const newObjs = myObjects.map(o => {
const obj = {};
for (let key in o) {
if (toKeep.has(key)) {
obj[key] = o[key];
}
}
return obj;
});
console.log(newObjs);

Merge items from two arrays based on matching ID

I have a data object like this :
{
"data1": [
[
"ID",
"name",
"Birthday"
],
[
"10",
"thomas",
"1992-03-17"
],
[
"11",
"Emily",
"2000-03-03"
]
],
"data2": [
[
"Balance",
"ID"
],
[
"$4500",
"10"
],
[
"$1500",
"13"
]
]
}
It contains two arrays data1 and data2.
The first row in each array is the name of the columns and the rest of the rows have the data (think of it like a table).
I want to compare the ID field in both arrays and if the IDs match then the final output will contain a column Balance with the balance corresponding to that ID and if the IDs don't match then the Balance will be $0.
Expected output:
{
"output": [
[
"ID",
"name",
"Birthday",
"Balance"
],
[
"10",
"thomas",
"1992-03-17",
"$4500" //ID 10 matched so the balance added here
],
[
"11",
"Emily",
"2000-03-03",
"0" //0 bcoz the ID 11 is not there in data2 array
]
]
}
I find this challenging to accomplish. Think of it like a LEFT-JOIN in MySQL.
I referred to this solution but it doesn't work in my case as I don't have the keys in my response.
EDIT: I also need to join the other fields as well.
You can use Array.prototype.map(), find, filter, slice, reduce, concat, includes and Object.assign().
This solution:
Handles arbitrary ordering of the items. The order is read from the headers.
Appends a Balance field only if there is one present in data2.
Joins all other fields (requested by OP, see comments below).
Takes default values as an input and uses them if the data is not present in data1 and data2.
function merge({ data1, data2 }, defaults) {
// get the final headers, add/move 'Balance' to the end
const headers = [...data1[0].filter(x => x !== 'Balance')]
.concat(data2[0].includes('Balance') ? ['Balance'] : []);
// map the data from data1 to an array of objects, each key is the header name, also merge the default values.
const d1 = data1.slice(1)
.map(x => x.reduce((acc, y, i) => ({ ...defaults, ...acc, [data1[0][i]]: y }), {}));
// map the data from data2 to an array of objects, each key is the header name
const d2 = data2.slice(1)
.map(x => x.reduce((acc, y, i) => ({ ...acc, [data2[0][i]]: y }), {}));
// combine d1 and d2
const output = d1.map((x, i) => { // iterate over d1
// merge values from d2 into this value
const d = Object.assign(x, d2.find(y => y['ID'] === x['ID']));
// return an array ordered according to the header
return headers.map(h => d[h]);
});
return { output: [headers, ...output] };
}
const test0 = {
data1: [[ "ID","name","Birthday","other"],["10","thomas","1992-03-17","empty"],["11","Emily","2000-03-03","empty"]],
data2: [["other", "ID", "Balance", "city"],["hello", "10", "$4500", "New York"],["world", "10","$8","Brazil"]]
};
const test1 = {
data1: [["ID","name","Birthday"],["10","thomas","1992-03-17"],["11","Emily","2000-03-03"]],
data2: [["other","ID"],["x","10"],["y","11"]]
};
console.log(merge(test0, { Balance: '$0' }));
console.log(merge(test1, { Balance: '$0' }));
const KEY_ID = "ID";
var data = {
"data1": [
[ "ID", "name", "Birthday" ],
[ "10", "thomas", "1992-03-17" ],
[ "11", "Emily", "2000-03-03" ]
],
"data2": [
[ "Balance", "ID" ],
[ "$4500", "10" ],
[ "$1500", "13" ]
]
}
var merged = Object.keys(data).map(function (key) {
var tmp = data[key].slice();
var heads = tmp.shift();
return tmp.map(function (item) {
var row = {};
heads.forEach(function (head, i) {
row[head] = item[i];
});
return row;
});
}).flat().reduce(function (acc, row) {
var found = acc.find(function (item) {
return row[KEY_ID] === item[KEY_ID];
})
if (!found) {
found = row;
acc.push(found);
} else {
Object.keys(row).forEach(function (head) {
found[head] = row[head];
});
}
return acc;
}, []);
console.log(merged);
This solution is scalable: if you add properties, it will scale the new format.
let a = { "data1": [ ... ],"data2": [ ...] }
let r = a.data1.reduce((r,u,i)=>{
if(i !== 0)
{
let entry = a.data2.filter((a)=> a[1]===u[0])
r.push([...u,entry.length ? entry[0][0] : 0])
}
return r
},[[
"ID",
"name",
"Birthday",
"Balance"
]])
You could abstract all table operations into a class-like:
function Table(array) {
const [head, ...values] = array;
const Entry =(entry) => ({
get(key) { return entry[ head.indexOf(key) ]; },
set(key, value) { entry[ head.indexOf(key) ] = value; }
});
return {
index(name) {
const result = {};
for(const value of values)
result[ value[ head.indexOf(name) ] ] = Entry(value);
return result;
},
*[Symbol.iterator]() {
for(const row of values)
yield Entry(row);
},
addRow(key) { head.push(key); }
};
}
Usable as:
const users = Table(obj.data1);
const balances = Table(obj.data2);
const balanceByID = balance.index("ID");
users.addRow("Balance");
for(const user of users)
user.set("Balance", balanceByID[ user.get("ID") ].get("Balance"));

Create JSON Array dynamically from an object

I have an object A as shown below.
var A = {
"1": [ "1_1", "1_2", "1_3" ],
"2": [ "2_1", "2_2" ]
};
Need to build a new array dynamically using js. Suppose
object A key should map to attribute text of Array AA and value should be to children as given below.
var AA = [
{
"text": "1",
"state": "open",
"children": [
{ "text": "1_1" },
{ "text": "1_2" },
{ "text": "1_3" }
]
},
{
"text": "2",
"state": "open",
"children": [
{ "text": "2_1" },
{ "text": "2_2" }
]
}
];
This is my function but its not working as expected. Could someone pls help?
function constructJSONArr() {
var A = {
"1": [ "1_1", "1_2", "1_3" ],
"2": [ "2_1", "2_2" ]
};
for (var key in A) {
var tempArr = [];
tempArr.push(key);
for (var i = 0; i < key.length; i++) {
return {
'text': key,
'state': 'closed',
'children': A[key].map(function(child) {
return {
'text': child
};
})
}
}
}
}
When you return inside a function, the function ends and returns immediately. In your case, the return inside the for loop causes the function to return the 1st key object. To solve this, you need to create the objects and push them into an arr. You can return freely inside Array.map() because each iteration invokes a function.
Fixed solution:
Iterate with for...in. Get the key. Push a new object into arr. Use the key as the text property, the state, and children. To create the children get the array from the original object by the key, and use Array.map() to generate the child objects. Return arr.
var A = {
"1": ["1_1", "1_2", "1_3"],
"2": ["2_1", "2_2"]
};
function constructJSONArr(A) {
var arr = [];
for (var key in A) {
arr.push({
text: key,
state: 'closed',
children: A[key].map(function(t) {
return {
text: t
};
})
});
}
return arr;
}
var result = constructJSONArr(A);
console.log(result);
ESNext solution
Use Object.entries() to get keys and respective values from the object A. Iterate the entries with two nested Array.map() calls. The 1st to create the outer object, and the 2nd to create the children.
const A = {
"1": ["1_1", "1_2", "1_3"],
"2": ["2_1", "2_2"]
};
const constructJSONArr = (obj) =>
Object.entries(obj).map(([text, children]) => ({
text,
state: 'closed',
children: children.map((text) => ({
text
}))
}));
var result = constructJSONArr(A);
console.log(result);
You can use Object.keys() to iterate through the object and Array.map to create the new array.
var A = {
"1": ["1_1", "1_2", "1_3"],
"2": ["2_1", "2_2"]
};
var transformed = Object.keys(A).map(key => {
return {
text: key,
state: "open",
children: A[key].map(value => {
return {
text: value
};
})
};
});
console.log(transformed);

Categories