I'm trying to find a way to convert this list of objects based on the group array. The tricky part I've found is iterating through the group Array and applying the object to more than one place if there are multiple groups.
I'm also trying to ignore any group that does not belong to anything. I've tried using the reduce function but I cannot get the iteration through the group array.
let cars =
[
{
"group":[],
"name": "All Makes",
"code": ""
},
{
"group":["Group A"],
"name": "BMW",
"code": "X821"
},
{
"group":["Group B"],
"name": "Audi",
"code": "B216"
},
{
"group":["Group B"],
"name": "Ford",
"code": "P385"
},
{
"group":["Group B", "Group C"],
"name": "Mercedes",
"code": "H801"
},
{
"group":["Group C"],
"name": "Honda",
"code": "C213"
}
]
To become this:
let cars = {
"Group A": [
{
name: "BMW",
code: "X821",
}
],
"Group B": [
{
name: "Audi",
code: "B216"
},
{
name: "Ford",
code: "P385"
},
{
name: "Mercedes",
code: "H801"
}
],
"Group C":[
{
name: "Mercedes",
code: "H801"
},
{
name:"Honda",
code: "C213"
}
]
};
I already tried using reduce to accomplish this but the grouping doesn't replicate if it's in more than one group.
let result = cars.reduce(function(x, {group, name}){
return Object.assign(x, {[group]:(x[group] || [] ).concat({group, name})})
}, {});
Any pointers to help with this would be much appreciated.
You can use .reduce() to loop through each car object in cars. For each group array for a given car, you can then use .forEach() to then add that group as a key to the accumulator. If the group has already been set in the accumulator, you can grab the grouped array of objects, otherwise, you can create a new array []. Once you have an array you can then add the object to the array using .concat(). Since we're using .forEach() on the group array, it won't add the object to the accumulated object if it is empty as .forEach() won't iterate over an empty array.
See example below:
const cars = [{ "group":[], "name": "All Makes", "code": "" }, { "group":["Group A"], "name": "BMW", "code": "X821" }, { "group":["Group B"], "name": "Audi", "code": "B216" }, { "group":["Group B"], "name": "Ford", "code": "P385" }, { "group":["Group B", "Group C"], "name": "Mercedes", "code": "H801" }, { "group":["Group C"], "name": "Honda", "code": "C213" } ];
const res = cars.reduce((acc, {group, ...r}) => {
group.forEach(key => {
acc[key] = (acc[key] || []).concat({...r}); // copy r so it is a different reference for each grouped array
});
return acc;
}, {});
console.log(res);
Some basic approach. #Nick's is much better.
let cars = [{
"group": [],
"name": "All Makes",
"code": ""
},
{
"group": ["Group A"],
"name": "BMW",
"code": "X821"
},
{
"group": ["Group B"],
"name": "Audi",
"code": "B216"
},
{
"group": ["Group B"],
"name": "Ford",
"code": "P385"
},
{
"group": ["Group B", "Group C"],
"name": "Mercedes",
"code": "H801"
},
{
"group": ["Group C"],
"name": "Honda",
"code": "C213"
}
]
let newCars = {};
cars.forEach(o => {
o.group.forEach(g => {
if (!newCars[g])
newCars[g] = [];
newCars[g].push({
name: o.name,
code: o.code
});
});
});
console.log(newCars);
Related
Is it possible to use the find() method within an array of depth x?
For example, suppose I have the following array of objects, call it test:
[
{
"id": "1",
"title": "First",
},
{
"id": "2",
"title": "Second",
"movies": [
{
"id": "3",
"title": "Happy Gilmore",
"Actors": [
{
"id": "4",
"title": "John Doe",
},
{
"id": "5",
"title": "Jane Doe",
},
],
"Producers": [
{
"id": "6",
"title": "Max Smith",
},
{
"id": "7",
"title": "Richard Rocky",
},
],
},
{
"id": "10",
"title": "Billy Madison",
"Actors": [
{
"id": "40",
"title": "John Smith",
},
{
"id": "50",
"title": "Alex Doe",
},
],
"Producers": [
{
"id": "60",
"title": "Bob Smith",
},
{
"id": "70",
"title": "Polly Rocky",
},
],
}
]
}
]
Suppose I am looking for the "2" id. I can use the find() method to search the first level of the array and return the desired object by doing test.find(element => element.id === "2").
However, suppose I am now looking for the occurrence where the id is 4. As you can see from the above JSON, that element is within a sub array within test. Is there a way therefore where I can still search through test to find the element where id=4?
find cannot do this, but you can use it in a recursive approach:
function findDeep(arr, predicate) {
let res = arr.find(predicate);
if (res !== undefined) return res;
for (let obj of arr) {
for (let value of Object.values(Object(obj)).filter(Array.isArray)) {
res = findDeep(value, predicate);
if (res !== undefined) return res;
}
}
}
let test = [{"id": "1","title": "First",},{"id": "2","title": "Second","movies": [{"id": "3","title": "Happy Gilmore","Actors": [{"id": "4","title": "John Doe",},{"id": "5","title": "Jane Doe",},],"Producers": [{"id": "6","title": "Max Smith",},{"id": "7","title": "Richard Rocky",},],},{"id": "10","title": "Billy Madison","Actors": [{"id": "40","title": "John Smith",},{"id": "50","title": "Alex Doe",},],"Producers": [{"id": "60","title": "Bob Smith",},{"id": "70","title": "Polly Rocky",},],}]}];
let res = findDeep(test, obj => obj.id == "4");
console.log(res);
Trying to create a new object, and then join all the objects from nested values from a JSON file.
The JSON data is rather large, so have taken a sample, and called it var items
Problem I am having is that the nested data is not updating the new object.
var items = [
{
"id": 11,
"title": "Fruit Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Fruit order for 1 person",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Melon",
"otherName": "Watermelon"
},
{
"itemName": "Apple",
"otherName": "Red apple"
}
]
},
{
"id": 12,
"title": "Canned Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Canned order for 2 people",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Tomatoes",
"otherName": "Diced tomato"
}
]
},
{
"id": 13,
"title": "Dairy Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Dairy Order for 2 people",
"storeNames": [
"Store 1"
],
"items": []
}
]
;
var copyItems = [];
for (let i = 0; i < items.length; i++) {
items[i].allItems = items[i].items;
copyItems.push(items[i])
}
console.log(copyItems);
var copyItems = copyItems.map(function(elem){
return elem.allItems;
}).join(",");
console.log(`These are the final items ${copyItems}`);
I am able to create the new object, and add the nested arrays to this. However I am trying to get the allItems object to display the information like the following:
[
{
"id": 11,
"allItems": "Melon, Apple",
"title": "Fruit Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Fruit order for 1 person",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Melon",
"otherName": "Watermelon"
},
{
"itemName": "Apple",
"otherName": "Red apple"
}
]
},
{
"id": 12,
"allItems": "Tomatoes",
"title": "Canned Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Canned order for 2 people",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Tomatoes",
"otherName": "Diced tomato"
}
]
},
{
"id": 13,
"allItems": "",
"title": "Dairy Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Dairy Order for 2 people",
"storeNames": [
"Store 1"
],
"items": []
}
]
Here is my JSFiddle: https://jsfiddle.net/buogdvx9/6/
Javascript is still a language I am learning and working through, and some things still catch me out.
Thank you.
You can use Array.map() to create the new array, then using some destructuring to create each new element in this array.
We create the allitems property on each new element by first mapping the sub items array to get a list of subitem names, then using Array.join() to create a comma separated string.
The arrow function you see as the first argument to Array.map is another way of writing function(args) { .. }.
const items = [ { "id": 11, "title": "Fruit Test", "releaseDateTime": "2021-10-21T10:50:00+09:30", "mainContent": "Fruit order for 1 person", "storeNames": [ "Store 1" ], "items": [ { "itemName": "Melon", "otherName": "Watermelon" }, { "itemName": "Apple", "otherName": "Red apple" } ] }, { "id": 12, "title": "Canned Test", "releaseDateTime": "2021-10-21T10:50:00+09:30", "mainContent": "Canned order for 2 people", "storeNames": [ "Store 1" ], "items": [ { "itemName": "Tomatoes", "otherName": "Diced tomato" } ] }, { "id": 13, "title": "Dairy Test", "releaseDateTime": "2021-10-21T10:50:00+09:30", "mainContent": "Dairy Order for 2 people", "storeNames": [ "Store 1" ], "items": [] } ];
const result = items.map(({ id, ...rest}) => {
return {
id,
allItems: rest.items.map(el => el.itemName).join(', '),
...rest
};
});
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Since you only want to update the existing object, using forEach to loop over each item in array, then loop over the prosperity with a map operator to get the array with itemName.
items.forEach((obj) => {
obj.allItems = obj.items.map((item) => item.itemName)
});
console.log(items)
Simple example:
// iterating over the items
for (let i = 0; i < items.length; i++) {
let currentItem = items[i];
currentItem.allItems = []; // adding new empty array
for (let j = 0; j < currentItem.items.length; j++) {
currentItem.allItems.push(currentItem.items[j].itemName);
}
}
Working Example:
var items = [
{
"id": 11,
"title": "Fruit Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Fruit order for 1 person",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Melon",
"otherName": "Watermelon"
},
{
"itemName": "Apple",
"otherName": "Red apple"
}
]
},
{
"id": 12,
"title": "Canned Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Canned order for 2 people",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Tomatoes",
"otherName": "Diced tomato"
}
]
},
{
"id": 13,
"title": "Dairy Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Dairy Order for 2 people",
"storeNames": [
"Store 1"
],
"items": []
}
]
;
// since you only want to update the existing object, using map to loop over each item in array
items.forEach((obj) => {
// using map to create the new array of just itemNames
obj.allItems = obj.items.map((item) => item.itemName)
});
console.log(items)
Just use the combination of Array.map and Array.join
Logic
Since you want to create a new array, run Array.map on the parent array.
On each nodes in the parent return the whole node with one extra key allItems.
For allItems create a new array from items array in each node and join then with space
var items = [{"id":11,"title":"Fruit Test","releaseDateTime":"2021-10-21T10:50:00+09:30","mainContent":"Fruit order for 1 person","storeNames":["Store 1"],"items":[{"itemName":"Melon","otherName":"Watermelon"},{"itemName":"Apple","otherName":"Red apple"}]},{"id":12,"title":"Canned Test","releaseDateTime":"2021-10-21T10:50:00+09:30","mainContent":"Canned order for 2 people","storeNames":["Store 1"],"items":[{"itemName":"Tomatoes","otherName":"Diced tomato"}]},{"id":13,"title":"Dairy Test","releaseDateTime":"2021-10-21T10:50:00+09:30","mainContent":"Dairy Order for 2 people","storeNames":["Store 1"],"items":[]}];
const newItems = items.map(node => ({ ...node, allItems: node.items.map(item => item.itemName).join(" ")}));
console.log(newItems);
This question already has answers here:
Get list of duplicate objects in an array of objects
(8 answers)
Closed 1 year ago.
I have this long list of array and I want to filter the object return by id. For example, I want to get objects with the same id, in this case object at index 0 and index 2. How can i achieve this? I have tried the for loop method but it's not going anywhere
var arr = [
{
"name": "David",
"last_updated": "2021-04-12 15:42:51",
"id": "175",
"class": "CLASS 2019",
"stops": [
{
"total": "29",
"graduated": "1900"
},
],
},
{
"name": "Cameron",
"last_updated": "2021-04-12 15:42:51",
"id": "180",
"class": "CLASS 2021",
"stops": [
{
"total": "40",
"graduated": "2500"
},
],
},
{
"name": "Rose",
"last_updated": "2021-04-12 15:42:51",
"id": "175",
"class": "CLASS 2008",
"stops": [
{
"total": "50",
"graduated": "1000"
},
],
},
This is a short snippet that I have in mind and tried. I'm aware that it doesn't make sense hence why I'm asking here. Any explanations and workarounds is very much appreciated
for(let i=0; i<arr.length; i++) {
if(arr[i].id === arr[i].id) {
console.log(arr[i])
}
}
Please correct me if I am misunderstanding here, but you simply want to filter the array of objects to only keep objects whose id value appears more than once in the array.
If that's the case, then my solution below should answer your question. Essentially, what it does is filter the source array by a map of all id values and filters to only objects whose id appears more than once. Using length - 1 works interchangeably with length > 1 here as subtracting 1 will product a falsy value 0 for those with only one instance of their id. The only difference here would be that this would not filter objects without an id property.
If you will be dealing with objects without an id property and would like to exclude those in the final result, change length - 1 to length > 1.
const arr = [
{ name: "David", last_updated: "2021-04-12 15:42:51", id: "175", class: "CLASS 2019", stops: [ { total: "29", graduated: "1900" } ] },
{ name: "Cameron", last_updated: "2021-04-12 15:42:51", id: "180", class: "CLASS 2021", stops: [ { total: "40", graduated: "2500" } ] },
{ name: "Rose", last_updated: "2021-04-12 15:42:51", id: "175", class: "CLASS 2008", stops: [ { total: "50", graduated: "1000" } ] }
];
const uniqObjs = [];
const dupeObjs = [];
arr.forEach(obj => [uniqObjs,dupeObjs][+(arr.map(obj => obj.id).filter(id => id === obj.id).length > 1)].push(obj));
console.log('uniqObjs:',uniqObjs);
console.log('dupeObjs:',dupeObjs);
You can use array.filter
var arr = [{
"name": "David",
"last_updated": "2021-04-12 15:42:51",
"id": "175",
"class": "CLASS 2019",
"stops": [{
"total": "29",
"graduated": "1900"
}, ],
},
{
"name": "Cameron",
"last_updated": "2021-04-12 15:42:51",
"id": "180",
"class": "CLASS 2021",
"stops": [{
"total": "40",
"graduated": "2500"
}, ],
},
{
"name": "Rose",
"last_updated": "2021-04-12 15:42:51",
"id": "175",
"class": "CLASS 2008",
"stops": [{
"total": "50",
"graduated": "1000"
}, ],
},
]
const id175 = arr.filter(item => item.id === '175');
console.log(id175)
This question already has answers here:
Group array items using object
(19 answers)
Closed 3 years ago.
I have an objects with similar ids
[{
"_id": "603",
"name": 'innova',
"type": 'suv',
"brand": 'toyota'
},
{
"_id": "902",
"name": 'i20',
"type": 'hashback',
"brand": 'hyundai'
},
{
"_id": "603",
"name": 'indigo',
"type": 'haskback',
"brand": 'toyota'
}]
should be converted to
[{
"_id": "603",
"name": ['innova', 'indigo'],
"type": ['suv', 'haskback'],
"brand": ['toyota', 'toyota']
}, {
"_id": "902",
"name": ['i20'],
"type": ['hashback'],
"brand": ['hyundai']
}]
i have tried using Object.keys and Object.Values by pushing them if id already exits but failed.
Use the reduce function to build an object _id based.
Then after use Object.values() to retrieve an object as you want
const data = [{
"_id": "603",
"name": 'innova',
"type": 'suv',
"brand": 'toyota'
},
{
"_id": "902",
"name": 'i20',
"type": 'hashback',
"brand": 'hyundai'
},
{
"_id": "603",
"name": 'indigo',
"type": 'haskback',
"brand": 'toyota'
}
]
const newData = data.reduce((acc, row) => {
if (!acc.hasOwnProperty(row._id)) {
// there is no rows with the _id, we push a row into our accumulator
acc[row._id] = {
"_id": row._id,
name: [row.name],
type: [row.type],
brand: [row.brand]
};
} else {
// as the row with the _id exists, we just push values in the arrays
acc[row._id].name.push(row.name);
acc[row._id].type.push(row.type);
acc[row._id].brand.push(row.brand);
}
return acc;
}, {});
console.log(Object.values(newData));
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);