create a new array from the keys of an Array's Object - javascript

I have a array of objects, and by using the foreach or map I want to create new array from its keys:
[{
"name": "Dentist Specialist",
"category": "Roles",
"path": "cde"
},
{
"name": "Root Canal Therapy",
"category": "Procedures",
"path": "abc"
},
{
"name": "Live Course",
"category": "Course Type",
"path": "mfg"
}]
From the above array I need a new ARRAY which will look like this:
[{
"Roles": "Dentist Specialist"
},
{
"Procedures": "Root Canal Therapy"
},
{
"Course Type": "Live Course"
}]
Just replace the 2nd key with the first key and remove the rest.

You can use map here to achieve the desired result.
arr.map(({ category, name }) => ({ [category]: name }));
or
arr.map((o) => ({ [o.category]: o.name }));
const arr = [
{
name: "Dentist Specialist",
category: "Roles",
path: "cde",
},
{
name: "Root Canal Therapy",
category: "Procedures",
path: "abc",
},
{
name: "Live Course",
category: "Course Type",
path: "mfg",
},
];
const result = arr.map((o) => ({ [o.category]: o.name }));
console.log(result);

Related

Groupby multiple keys with lodash or plain js

I've been trying to group by multiple keys with lodash but it's not returning the correct results. The aim is to group by state and details but in this case it's joining/grouping docIds 333333 and 444444 which although have equal state (rejected), don't have the same id's in details (=> different people) so they don't share both conditions: state and id's in details.
It seems my current code does work with multiple criteria but once only one of the conditions exists it still performs the grouping, while I wanted to only group if both criteria were satisfied.
As for lodash approach It should show:
[
{
"docId": "222222,1111111",
"details": [
{
"id": 20656,
"type": "Claimant",
"name": "First Name Last Name"
},
{
"id": 10000,
"type": "Fellow",
"name": "Fellow First Name Fellow Last Name"
}
],
"state": "accepted",
},
{
"docId": "333333",
"details": [
{
"id": 10000,
"type": "Fellow",
"name": "Fellow First Name Fellow Last Name"
}
],
"state": "rejected",
},
{
"docId": "444444",
"details": [
{
"id": 20656,
"type": "Claimant",
"name": "First Name Last Name"
},
],
"state": "rejected",
}
]
I'm open to use plain js and have no problem in getting for example "docId": [{"333333"},{"444444"}] instead of comma separated values , or slightly different final result as long as the grouping in the end obeys to the same rules but I wasn't able to achieve the intended result either with plain js so I moved to lodash which seemed simpler.
In the end I will be needing some sorting to prioritize state, then groups with only one person in details and when one person, prioritize the ones with claimant but that's something that should be done afterwards, right?
Help would be much appreciated.
const data = [
{
docId: 222222,
state: "accepted",
details: [
{
id: 20656,
type: "Claimant",
name: "First Name Last Name",
},
{
id: 10000,
type: "Fellow",
name: "Fellow First Name Fellow Last Name",
}
]
},
{
docId: 1111111,
state: "accepted",
details: [
{
id: 10000,
type: "Fellow",
name: "Fellow First Name Fellow Last Name",
},
{
id: 20656,
type: "Claimant",
name: "First Name Last Name",
}
]
},
{
docId: 333333,
state: "rejected",
details: [
{
id: 10000,
type: "Fellow",
name: "Fellow First Name Last Name",
}
]
},
{
docId: 444444,
state: "rejected",
details: [
{
id: 20656,
type: "Claimant",
name: "First Name Last Name",
}
]
}
];
const grouped = _(data)
.groupBy(({details,state}) => `${details},${state}`)
.map((value, key) => ({
docId: _.map(value, 'docId').join(','),
details: value[0].details,
state: value[0].state,
document_file_name: value[0].document_file_name,
}))
.value()
console.log(grouped)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
First collect id by state and docId.
Second group collected data by state and sorted id and build new objects.
const
data = [{ docId: 222222, document_file_name: "4020653_FileName.pdf", document_updated_at: "2020-07-08T19:41:28.385Z", state: "accepted", details: [{ id: 20656, type: "Claimant", name: "First Name Last Name" }, { id: 10000, type: "Fellow", name: "Fellow First Name Fellow Last Name" }] }, { docId: 1111111, document_file_name: "4020600_FileName.pdf", document_updated_at: "2020-07-08T19:41:28.385Z", state: "accepted", details: [{ id: 10000, type: "Fellow", name: "Fellow First Name Fellow Last Name" }, { id: 20656, type: "Claimant", name: "First Name Last Name" }] }, { docId: 333333, document_file_name: "4020890_FileName.pdf", document_updated_at: "2020-07-08T19:41:28.385Z", state: "rejected", details: [{ id: 10000, type: "Fellow", name: "Fellow First Name Last Name" }] }, { docId: 444444, document_file_name: "4020672_FileName.pdf", document_updated_at: "2020-07-08T19:41:28.385Z", state: "rejected", details: [{ id: 20656, type: "Claimant", name: "First Name Last Name" }] }],
ids = {},
temp = data.reduce((r, { docId, state, details }) => {
const key = [state, docId].join('|');
details.forEach(o => {
ids[o.id] = o;
(r[key] ??= []).push(o.id);
});
return r;
}, {}),
grouped = Object.values(Object
.entries(temp)
.reduce((r, [k, v]) => {
const
[state, docId] = k.split('|'),
key = [state, v.sort().join('|')].join('#');
r[key] ??= { docId: '' , details: v.map(id => ids[id]), state };
r[key].docId += (r[key].docId && ', ') + docId;
return r;
}, {}));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

JSON data, need modifications

I have a problem with converting Json data , I get a list objects with different categories for each category there are two versions , So I need to modify the in a way to get list of objects where each object key is the category and its value is list of its corresponding versions and details.
data = [
{
"category1": {
"name": "category1",
"id": 1,
"last_sync_date": "None",
"version": "Version 1"
}
},
{
"category1": {
"name": "category1",
"id": 2,
"last_sync_date": "None",
"version": "Version 2"
}
},
{
"category2": {
"name": "category2",
"id": 1,
"last_sync_date": "None",
"version": "Version 1"
}
},
{
"category3": {
"name": "category3",
"id": 2,
"last_sync_date": "None",
"version": "Version 2"
}
},
]
To:
data = {
category1: [
{
id: "1",
name: "category1",
last_sync_date: "01/01/2022 10:45 AM",
version: "Version 1"
},
{
id: "2",
name: "category1",
last_sync_date: "01/01/2022 10:45 AM",
version: "Version 2"
},
],
category2:[
{
id: "3",
name: "category2",
last_sync_date: "01/01/2022 10:45 AM",
version: "Version 1"
},
],
category3:[
{
id: "4",
name: "category3",
last_sync_date: "01/01/2022 10:45 AM",
version: "Version 1"
},
],
}
Note: I was able to provide only one unit of data.
This is not JSON btw, this is just a JavaScript Array of objects that needs to be converted into another structure.
This can be done with any of the loop logics. I can show you one example with Array.reduce
Logic
Loop through data array using Array.reduce
Take each object and get the keys of each object.
Check whether this key exist in out accumulator object
If the key exist, push the Object to existing key, else create an empty array as the value for that key and push our object to the emty array just created.
const data = [
{
category1: { name: "category1", id: 1, last_sync_date: "None", version: "Version 1" },
},
{
category1: { name: "category1", id: 2, last_sync_date: "None", version: "Version 2" },
},
{
category2: { name: "category2", id: 1, last_sync_date: "None", version: "Version 1" },
},
{
category3: { name: "category3", id: 2, last_sync_date: "None", version: "Version 2" },
},
];
const output = data.reduce((acc, curr) => {
const keys = Object.keys(curr);
keys.forEach((key) => {
acc[key] = acc[key] || [];
acc[key].push(curr[key]);
});
return acc;
}, {});
console.log(output);
The following uses Array#reduce to enumerate the data. It pulls out the category and the object associated with each item. The objects are then added to a result object, keyed by category and grouped within arrays.
Does this do what you want?
const data = [{
"category1": {
"name": "category1",
"id": 1,
"last_sync_date": "None",
"version": "Version 1"
}
}, {
"category1": {
"name": "category1",
"id": 2,
"last_sync_date": "None",
"version": "Version 2"
}
}, {
"category2": {
"name": "category2",
"id": 1,
"last_sync_date": "None",
"version": "Version 1"
}
}, {
"category3": {
"name": "category3",
"id": 2,
"last_sync_date": "None",
"version": "Version 1"
}
}, ]
const group = (data) =>
data.reduce((acc, c) => {
const [[category, value]] = Object.entries(c)
acc[category]
? acc[category].push(value)
: acc[category] = [value]
return acc
}, {})
console.log(group(data))
This is not a JSON structure. Input is an array of objects. According to your description you just want to convert it to another structure.
Here is a sample solution of this problem.
// const data = [{}, {}...]; Input array of objects
const ans = {};
data.map(object => {
const keys = Object.keys(object);// to get keys of current object
const firstKey = keys[0];
const value = object[firstKey];
if (ans[firstKey] == undefined) {
ans[firstKey] = [value];
} else {
ans[firstKey].push(value);
}
})
console.log(ans);

Return name and id property value of all arrays inside object

How to return name and id property value of all arrays? The idea is to make a single map of all these arrays and return the id and name?
Something like this
filters.[key].map((option, index) => (
<ItemFilter key={index}>{option}</ItemFilter>
))
I have this array object
filters: {
"services": [
{
"id": "1b975589-7111-46a4-b433-d0e3c0d7c08c",
"name": "Bank"
},
{
"id": "91d4637e-a17f-4b31-8675-c041fe06e2ad",
"name": "Income"
}
],
"accountTypes": [
{
"id": "1f34205b-2e5a-430e-982c-5673cbdb3a68",
"name": "Digital Account"
}
],
"channels": [
{
"id": "875f8350-073e-4a20-be20-38482a86892b",
"name": "Chat"
}
]
}
You can use flatMap or flat to achieve the desired result.
Object.values(obj.filters).flatMap(v => v)
or
Object.values(obj.filters).flat()
const obj = {
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
};
const result = Object.values(obj.filters).flatMap(v => v);
console.log(result);
If option is referring to name in your example code it could look something like this:
Object.values(
{
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
}.filters
)
.flat()
.map(({ name, index }) => <ItemFilter key={index}>{name}</ItemFilter>);

Combine and flatten elements of array

With the below JSON content that is actually coming from an API but I'm using a json file for testing. I would like to combine the primary key and flatten the ItemList.
[{
"PrimaryKey": "123",
"ItemList": [
{
"SecondaryKey": "ABC",
"Name": "Item1",
"Description": "Sample item"
},
{
"SecondaryKey": "DEF",
"Name": "Item2",
"Description": "Another sample item"
}
],
"IgnoreThis": [
{
"SomeData": "Some Data"
}
]
}]
The output I would like is:
[{
"PrimaryKey": 123,
"SecondaryKey": "ABC",
"Name": "Item1",
"Description": "Sample Item"
},
{
"PrimaryKey": 123,
"SecondaryKey": "DEF",
"Name": "Item2",
"Description": "Another sample item"
}]
I've got the Item list being flattened by:
let items = [];
items.push(JSON.parse(fs.readFileSync('./items.json')));
let result = items.reduce((r, obj) => r.concat(obj.ItemList), []);
I've tried to use items.map to get the desired output nothing has worked, I don't think I understand how to chain .map and .reduce effectively as I get undefined as the result.
Any ideas how I can achieve this output?
You can do this by running map twice: get the PrimaryKey from the first map, then add it to all the objects inside the second map, then you flatten the array you got from the previous stage.
const data = [
{
PrimaryKey: "123",
ItemList: [
{
SecondaryKey: "ABC",
Name: "Item1",
Description: "Sample item",
},
{
SecondaryKey: "DEF",
Name: "Item2",
Description: "Another sample item",
},
],
IgnoreThis: [
{
SomeData: "Some Data",
},
],
},
{
PrimaryKey: "456",
ItemList: [
{
SecondaryKey: "ABC",
Name: "Item1",
Description: "Sample item",
},
{
SecondaryKey: "DEF",
Name: "Item2",
Description: "Another sample item",
},
],
IgnoreThis: [
{
SomeData: "Some Data",
},
],
},
];
const result = data.map(({ PrimaryKey, ItemList }) => ItemList.map(item => ({
PrimaryKey,
...item,
}))).flat();
console.log(result);

Object transformation using a functional approach

I am reading a simple data set from a data.txt file. I would like to take this data and transform it into a specific object as per my example below. I have managed to get it into a somewhat usable JSON object but this is not ideal. I have included an example of the desired object.
Here is my app.js file:
let output = fs.readFileSync('./data.txt', 'UTF8')
.trim()
.split('\r\n')
.map((line) => line.split(';'))
.reduce((customers, line) => {
customers.push({
name: line[0],
product: [{
item: line[1],
serial: line[2],
year: line[3]
}]
})
return customers
}, [])
console.log(JSON.stringify(output, null, 2))
This currently the above NodeJs code returns the following array object:
[
{
"name": "Nancy",
"product": [
{
"item": "Macbook Pro",
"serial": "A34D05980FCD4303",
"year": "2019"
}
]
},
{
"name": "Nancy",
"product": [
{
"item": "iPad",
"serial": "O0403X3028423C92",
"year": "2015"
}
]
},
{
"name": "Nancy",
"product": [
{
"item": "iPhone",
"serial": "X3830238S3309230",
"year": "2017"
}
]
},
{
"name": "John",
"product": [
{
"item": "Macbook Pro",
"serial": "X2020J393983H380",
"year": "2013"
}
]
},
{
"name": "John",
"product": [
{
"item": "iPhone",
"serial": "X38320093X032309",
"year": "2015"
}
]
},
{
"name": "fluffikins",
"product": [
{
"item": "iMac",
"serial": "F392D392033X3232",
"year": "2013"
}
]
},
{
"name": "fluffikins",
"product": [
{
"item": "iPad",
"serial": "FE322230D3223S21",
"year": "2011"
}
]
}
]
What I am trying to do is get the below object returned - ideally still following the same functional approach:
[
{
"name": "Nancy",
"product": [
{
"item": "Macbook Pro",
"serial": "A34D05980FCD4303",
"year": "2019"
},
{
"item": "iPad",
"serial": "O0403X3028423C92",
"year": "2015"
},
{
"item": "iPhone",
"serial": "X3830238S3309230",
"year": "2017"
}
]
},
{
"name": "John",
"product": [
{
"item": "Macbook Pro",
"serial": "X2020J393983H380",
"year": "2013"
},
{
"item": "iPhone",
"serial": "X38320093X032309",
"year": "2015"
}
]
},
{
"name": "fluffikins",
"product": [
{
"item": "iMac",
"serial": "F392D392033X3232",
"year": "2013"
},
{
"item": "iPad",
"serial": "FE322230D3223S21",
"year": "2011"
}
]
}
]
Here is my mock data set that lives in data.txt
Nancy;Macbook Pro;A34D05980FCD4303;2019
Nancy;iPad;O0403X3028423C92;2015
Nancy;iPhone;X3830238S3309230;2017
John;Macbook Pro;X2020J393983H380;2013
John;iPhone;X38320093X032309;2015
fluffikins;iMac;F392D392033X3232;2013
fluffikins;iPad;FE322230D3223S21;2011
Instead of an array you can use Map in reduce as accumulator, use name as key in Map and club value of all keys, finally just get the values Map to get desired output
const data = `Nancy;Macbook Pro;A34D05980FCD4303;2019
Nancy;iPad;O0403X3028423C92;2015
Nancy;iPhone;X3830238S3309230;2017
John;Macbook Pro;X2020J393983H380;2013
John;iPhone;X38320093X032309;2015
fluffikins;iMac;F392D392033X3232;2013
fluffikins;iPad;FE322230D3223S21;2011`
const final = data.split('\n')
.map(v => v.split(';'))
.reduce((op, [name, item, serial, year]) => {
let obj = { item, serial, year }
if (op.has(name)) {
op.get(name).products.push(obj)
} else{
op.set(name,{name, products:[obj]})
}
return op
}, new Map())
console.log([...final.values()])
Here is a "functional version" that utilizes a Map to find duplicates in O(1):
(map => (
fs.readFileSync('./data.txt', 'UTF8')
.trim()
.split('\r\n')
.map((line) => line.split(';'))
.forEach(([name, item, serial, year]) =>
map.has(name)
? map.get(name).product.push({ item, serial, year })
: map.set(name, { name, product: [{ item, serial, year }] })
),
[...map.values()]
)(new Map)
But seriously, whats so bad about imperative style?:
const customers = new Map;
const entries = fs.readFileSync('./data.txt', 'UTF8')
.trim()
.split('\r\n');
for(const entry of entries) {
const [name, item, serial, year] = entry.split(";");
const product = { item, serial, year };
if(customers.has(name)) {
customers.get(name).product.push(product);
} else customers.set(name, { name, product: [product] });
}
const result = [...customers.values()];
You can modify the .reduce function to only add a new item to the array if there isn't one with that name. If there is, just add the product to that item's product array.
const data = `Nancy;Macbook Pro;A34D05980FCD4303;2019
Nancy;iPad;O0403X3028423C92;2015
Nancy;iPhone;X3830238S3309230;2017
John;Macbook Pro;X2020J393983H380;2013
John;iPhone;X38320093X032309;2015
fluffikins;iMac;F392D392033X3232;2013
fluffikins;iPad;FE322230D3223S21;2011`;
const result = data.trim()
.split('\n')
.map((line) => line.split(';'))
.reduce((customers, line) => {
const product = {
item: line[1],
serial: line[2],
year: line[3]
};
const customer = customers.find(({
name
}) => name === line[0]);
if (customer) {
customer.product.push(product);
} else {
customers.push({
name: line[0],
product: [product]
});
}
return customers
}, []);
console.log(result);

Categories