I have an array of objects. I want to group them by a specific field.
[
{
"name": "JOHN",
"type": 1,
"sum": 5
},
{
"name": "SERA",
"type": 1,
"sum": 43
},
{
"name": "SERA",
"type": 2,
"sum": 129
},
{
"name": "JOHN",
"type": 2,
"sum": 200
}
]
The output I expect for grouping by name attribute is as follows.
{
// Group #1
"JOHN": [
{
"type": 2,
"sum": 200
}
{
"type": 1,
"sum": 5
}
],
// Group #2
"SERA":[
{
"type": 1,
"sum": 43
},
{
"type": 2,
"sum": 129
},
]
}
I used nested loops, but unfortunately the execution speed was slow and it did not give the right results.
As if you mentioned, we can use an object instead of an array for the most outer wrapper. And also swap inside one object to an array, then this is a possible solution.
var data = [{"name": "JOHN","type": 1,"sum": 5},{"name": "SERA","type": 1,"sum": 43},{"name": "SERA","type": 2,"sum": 129},{"name": "JOHN","type": 2,"sum": 200}];
var newData = {};
data.forEach( (item) => {
if (!(item['name'] in newData)) {
newData[item['name']] = [];
}
newData[item['name']].push(
{
'type': item['type'],
'sum' : item['sum']
}
);
});
console.log(newData);
Your proposed output structure is not valid, however using Array.reduce you can create an object in which all the properties are arrays of objects:
const data = [
{
"name": "JOHN",
"type": 1,
"sum": 5
},
{
"name": "SERA",
"type": 1,
"sum": 43
},
{
"name": "SERA",
"type": 2,
"sum": 129
},
{
"name": "JOHN",
"type": 2,
"sum": 200
}
];
const result = data.reduce((c, {name, type, sum}) => {
c[name] = c[name] || [];
c[name].push({type, sum});
return c;
}, {});
console.log(result);
One more way with forEach, destructuring and ?? operator
const merge = (arr) => {
const obj = {};
arr.forEach(({ name, ...rest }) => (obj[name] ??= []).push(rest));
return obj;
};
const data = [
{
name: "JOHN",
type: 1,
sum: 5,
},
{
name: "SERA",
type: 1,
sum: 43,
},
{
name: "SERA",
type: 2,
sum: 129,
},
{
name: "JOHN",
type: 2,
sum: 200,
},
];
console.log(merge(data));
You can use this function which take advantage of Array.prototype.reduce to transform the initial data to another structure of array.
let data = [
{
"name": "JOHN",
"type": 1,
"sum": 5
},
{
"name": "SERA",
"type": 1,
"sum": 43
},
{
"name": "SERA",
"type": 2,
"sum": 129
},
{
"name": "JOHN",
"type": 2,
"sum": 200
}
];
function groupedBy(data, field) {
let fieldValues = [...data].reduce((acc, current) => {
return acc.concat(current[field]);
}, []).filter((value, index, self) => {
return self.indexOf(value) === index;
});
let results = fieldValues.reduce((acc, item) => {
let items = [...data].filter(el => {
return el.name === item;
});
items.forEach(i => delete i.name);
return Object.assign(acc, { [item]: items});
}, {});
return results;
}
console.log(groupedBy(data, "name"));
Related
Just want to remove all the items other than 14 from the parentId: 1001 and add that item to another object.
I want to filter the array without affecting the source array.
var Data = [{
"id": 1001,
"text": "A",
"items": [
{ "id": 13, "text": "Thirteen" },
{ "id": 14, "text": "Fourteen" },
{ "id": 15, "text": "Fifteen", }
]
},
{
"id": 1002,
"text": "B",
"items": [
{ "id": 21, "text": "TwentyOne" },
{ "id": 22, "text": "TwentyTwo" },
{ "id": 23, "text": "TwentyThree", }
]
}
]
var childId = 14;
Data.items.filter((x) => {
return x.id != childId;
})
//this is affecting the source array (Data)
//after searching on internet found a solution
Data.items.filter((x) => {
return x.id childId;
}).map(function(x) {
return x
});
Your Data has no items property: it is an array, so you actually have Data[0].items, Data[1].items, ...
NB: it is common practice to use camelCase for such variable names, and reserve PascalCase for constructors/classes
Here is how you could do it:
const data = [{"id": 1001,"text": "A","items": [{ "id": 13, "text": "Thirteen" }, { "id": 14, "text": "Fourteen" }, { "id": 15, "text": "Fifteen", }]},{"id": 1002,"text": "B","items": [{ "id": 21, "text": "TwentyOne" }, { "id": 22, "text": "TwentyTwo" }, { "id": 23, "text": "TwentyThree", }]}]
const childId = 14;
const newData = data.map(obj => ({
...obj,
items: obj.items.filter(x => x.id != childId)
}));
console.log(newData);
As you want to filter out a few items from an array object and want to add those into another object.
You can also achieve this requirement by doing a deep copy of an original array with the help of structuredClone() API and then iterating it using Array#forEach method.
Live demo :
const data=[
{
"id":1001,
"text":"A",
"items":[
{
"id":13,
"text":"Thirteen"
},
{
"id":14,
"text":"Fourteen"
},
{
"id":15,
"text":"Fifteen",
}
]
},
{
"id":1002,
"text":"B",
"items":[
{
"id":21,
"text":"TwentyOne"
},
{
"id":22,
"text":"TwentyTwo"
},
{
"id":23,
"text":"TwentyThree",
}
]
}
];
const clone = structuredClone(data);
let remainingItems = [];
clone.forEach(obj => {
if (obj.id === 1001) {
remainingItems = obj.items.filter(({ id }) => id !== 14);
obj.items = obj.items.filter(({ id }) => id === 14);
} else {
obj.items = [...obj.items, ...remainingItems];
}
})
console.log('cloned data_____', clone);
console.log('source data_____', data);
I have an array of objects called orders:
const orders = [
{
"order_id": 47445,
"order_type": "Wholesale",
"items": [
{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
}
]
}
];
I need to get the quantity when both the order_type (Wholesale) and items.detail.ID (13363) match.
I have so far tried the following:
const result = orders.find(item => item.order_type == "Wholesale").items
.reduce((total, item) => {
if(item.detail.ID == 13363) {
return item.quantity;
}
}, 0);
Where result correctly returns 4
My issue, and I'm sure I am missing something very simple is that when I have multiple items in my orders array, it fails.
const orders = [
{
"order_id": 47445,
"order_type": "Wholesale",
"items": [
{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
},
{
"id": 56,
"department": "Womens",
"type": "Skirt",
"quantity": 12,
"detail": {
"ID": 76884,
"On Sale": 0,
}
},
{
"id": 89,
"department": "Mens",
"type": "Shirts",
"quantity": 20,
"detail": {
"ID": 98223,
"On Sale": 1,
}
}
]
}
];
The same
const result = orders.find(item => item.order_type == "Wholesale").items
.reduce((total, item) => {
if(item.detail.ID == 13363) {
return item.quantity;
}
}, 0);
returns undefined
Thank you
The find helper just returns the first match, so you need to use another helper like filter, like this:
const ID = 13363;
const result = orders
.filter((order) => order.order_type === 'Wholesale')
.reduce((acc, curr) => {
const items = curr.items.filter((item) => item.detail.ID === ID);
console.log(items);
// You can sum the matching items and then push them into the acc array
const quantity = items.reduce((sum, item) => (sum += item.quantity), 0);
acc.push(quantity);
return acc;
}, []);
This will return an array of matching quantities.
Not sure about the use case but here you go
const result = orders.find(item => item.order_type == "Wholesale").items
.reduce((total, item) => {
if (item.detail.ID == 13363) {
total += item.quantity;
}
return total
}, 0);
You can even create a function to make the search dynamic.
const orders = [
{
"order_id": 47445,
"order_type": "Wholesale",
"items": [
{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
},
{
"id": 56,
"department": "Womens",
"type": "Skirt",
"quantity": 12,
"detail": {
"ID": 76884,
"On Sale": 0,
}
},
{
"id": 89,
"department": "Mens",
"type": "Shirts",
"quantity": 20,
"detail": {
"ID": 98223,
"On Sale": 1,
}
}
]
}
];
findMyItem=( ID )=>{
var result = null ;
const result2 = orders.find(item => item.order_type == "Wholesale").items
.map(( item) => {
if(item.detail.ID == ID ) {
result = item.quantity;
}
}, 0);
return result ;
}
console.log( "result" ,findMyItem( 13363 ) )
console.log( "result" ,findMyItem( 98223) )
console.log( "result" ,findMyItem( 76884) )
You could use Array.find() on the orders array to find the correct order, searching for the first order that matches both the order_type and has an item matching the desired itemId (using Array.some()).
If this order exists, we can then find the corresponding item quantity using .find() again,
const orders = [ { "order_id": 47445, "order_type": "Wholesale", "items": [ { "id": 9, "department": "Womens", "type": "Dress", "quantity": 4, "detail": { "ID": 13363, "On Sale": 1, } }, { "id": 56, "department": "Womens", "type": "Skirt", "quantity": 12, "detail": { "ID": 76884, "On Sale": 0, } }, { "id": 89, "department": "Mens", "type": "Shirts", "quantity": 20, "detail": { "ID": 98223, "On Sale": 1, } } ] } ]
function findItemQuantity(orders, orderType, itemId) {
// Find the first order with the right order_type and containing the right item id
const order = orders.find(order => order.order_type = orderType && order.items.some(item => item.detail.ID === itemId));
if (!order) {
return null;
}
const item = order.items.find(item => item.detail.ID === itemId);
if (!item) {
return null;
}
return item.quantity;
}
console.log("Quantity found:", findItemQuantity(orders, 'Wholesale', 13363))
console.log("Quantity found:", findItemQuantity(orders, 'Wholesale', 76884))
const result = orders
.filter(order => order.order_type == "Wholesale")
.map(order => order.items.find(item => item.detail.ID == 13363))
.filter(item => item)
.reduce((total, { quantity }) => quantity + total, 0);
const orders = [{
"order_id": 47445,
"order_type": "Wholesale",
"items": [{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
}]
},
{
"order_id": 47445,
"order_type": "Whole",
"items": [{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
}]
}
]
const result = orders.reduce(v => {
return v.items.map(a => {
if (v.order_type == 'Wholesale' && a.detail.ID == 13363) {
return v
}
})
})
console.log(result)
const orders = [{
"order_id": 47445,
"order_type": "Wholesale",
"items": [{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
}]
}];
var result = null;
const result2 = orders.find(item => item.order_type == "Wholesale").items
.map((item) => {
if (item.detail.ID == 98223) {
result = item.quantity;
}
}, 0);
console.log("result", result)
I have an array like this:
[
{
"costs": [{
"value": "80"
}],
"id": 4,
"name": "Subscription Fee",
"month": "March"
},
[
{
"costs": [{
"value": "200"
}],
"id": 2,
"name": "Tution",
"month": "March"
},
{
"costs": [{
"value": "10"
}],
"id": 11,
"name": "DEMO"
}
]
]
I need to have sumation of all the values from costs. How can i do that?
const data = [
{"costs":[{"value":"80"}],"id":4,"name":"Subscription Fee","month":"March"},
[
{"costs":[{"value":"200"}],"id":2,"name":"Tution","month":"March"},
{"costs":[{"value":"10"}],"id":11,"name":"DEMO"}
]
];
// flatten the arrays to get a list of objects
// iterate over this list
const res = data.flat().reduce((total, { costs = [] }) => {
// add the values of this item's costs with total
costs.forEach(({ value = 0 }) => total += +value);
return total;
}, 0);
console.log(res);
I have two arrays with data from my database:
Cases array and photo's array from api:
{
"cases": [{
"id": 3,
"photo_id": 14
}, {
"id": 2,
"photo_id": 0
}, {
"id": 1,
"photo_id": 13
}],
"photos": [{
"id": 6,
"file": "\/images\/1556196076cache_f50f03558d201b8eb2a9af90f0838cee.png"
}, {
"id": 11,
"file": "\/images\/1556198414cache_702c216fa5a4d75d74db237ddf97b012.png"
}, {
"id": 12,
"file": "\/images\/1556198946cache_702c216fa5a4d75d74db237ddf97b012.png"
}, {
"id": 13,
"file": "\/images\/1556726055dewekkpot.nl_short.jpg"
}, {
"id": 14,
"file": "\/images\/1556791722dewekkpot.nl_short.jpg"
}]
}
Now if any photo_id from the cases array matches any id from the photos array with the same value, i have to extract the file key from that specific index with the matching id. And push them as a new key value pair to the cases array at the correct index.
What i now have is the following:
this.state = {
cases: [],
photos: [],
};
getCases() {
axios
.get('/cases/api')
.then(response => {
this.setState({
cases: response.data.cases,
photos: response.data.photos,
});
console.log(this.state.cases);
})
}
addPhotos() {
var photoIds = this.state.photos.map(function (player) {
return player.id;
});
var casesIds = this.state.cases.map(function (player) {
return player.photo_id;
});
casesIds = casesIds.filter(function (item) {
return photoIds.includes(item);
});
console.log(casesIds);
}
The output of this are the values that exists in the cases array from the photos array.
so [13, 14].
what should i do next?
Thanks in advance!
You can filter the objects as below snippet and use it to render your view.
const obj = {
"cases": [{
"id": 3,
"photo_id": 14
}, {
"id": 2,
"photo_id": 0
}, {
"id": 1,
"photo_id": 13
}],
"photos": [{
"id": 6,
"file": "\/images\/1556196076cache_f50f03558d201b8eb2a9af90f0838cee.png"
}, {
"id": 11,
"file": "\/images\/1556198414cache_702c216fa5a4d75d74db237ddf97b012.png"
}, {
"id": 12,
"file": "\/images\/1556198946cache_702c216fa5a4d75d74db237ddf97b012.png"
}, {
"id": 13,
"file": "\/images\/1556726055dewekkpot.nl_short.jpg"
}, {
"id": 14,
"file": "\/images\/1556791722dewekkpot.nl_short.jpg"
}]
};
const photoIds = obj.cases.reduce((acc, val) => {
acc[val.photo_id] = val;
return acc;
}, {});
const res = obj.photos.filter(val => photoIds[val.id]);
console.log(res)
Normalise your data. Convert your photos array to an object with photo_id as keys, for easy access.
You could do something like the following:
addPhotos() {
var photosMap = this.state.photos.reduce(function (acc, each) {
acc[each.id] = each; // or you could just save the corresponding filename
return acc;
}, {});
var photoFiles = this.state.cases.reduce(function (acc, each) { // you could alternatively use Array.filter too.
if (photosMap[each.photo_id]) {
acc.push(photosMap[each.photo_id]);
}
return acc;
}, []);
console.log(photoFiles);
}
You can use Array.map() and Array.find() like this:
const newCases = this.state.cases.map(({ photo_id, ...rest }) => {
const obj = { ...rest };
this.state.photos.find(ph => {
if(ph.id === photo_id) {
obj.file = ph.file;
return true;
}
});
return obj;
});
Live example:
const data = {
"cases": [{
"id": 3,
"photo_id": 14
}, {
"id": 2,
"photo_id": 0
}, {
"id": 1,
"photo_id": 13
}],
"photos": [{
"id": 6,
"file": "\/images\/1556196076cache_f50f03558d201b8eb2a9af90f0838cee.png"
}, {
"id": 11,
"file": "\/images\/1556198414cache_702c216fa5a4d75d74db237ddf97b012.png"
}, {
"id": 12,
"file": "\/images\/1556198946cache_702c216fa5a4d75d74db237ddf97b012.png"
}, {
"id": 13,
"file": "\/images\/1556726055dewekkpot.nl_short.jpg"
}, {
"id": 14,
"file": "\/images\/1556791722dewekkpot.nl_short.jpg"
}]
};
const newCases = data.cases.map(({ photo_id, ...rest }) => {
const obj = { ...rest };
data.photos.find(ph => {
if(ph.id === photo_id) {
obj.file = ph.file;
return true;
}
});
return obj;
});
console.log(newCases);
function addPhotos(cases, photos){
return cases.map(function (currentCase) {
const foundPhoto = photos.find(function(currentPhoto){
return currentPhoto.id === currentCase.photo_id;
});
currentCase.photo_path = foundPhoto? foundPhoto.file : "/images/blank_image.jpg";
return currentCase;
});
};
console.log(addPhotos(this.state.cases, this.state.photos));
//Output: [{"id":3,"photo_id":14,"photo_path":"/images/1556791722dewekkpot.nl_short.jpg"},{"id":2,"photo_id":0,"photo_path":"images/blank_image.jpg"},{"id":1,"photo_id":13,"photo_path":"/images/1556726055dewekkpot.nl_short.jpg"}]
I want to get the total length number of items under arraycompany for example in below example it should be 5, do you have an idea how to do it in java script. thanks
this is my json:
var json ={
"market": [
{
"company": [
{
"name": "A"
},
{
"name": "B"
},
{
"name": "C"
}
]
},
{
"company": [
{
"name": "D"
},
{
"name": "E"
}
]
}
]
}
json.market.reduce((acc, market) => acc + market.company.length, 0)
Use reduce function and update the value of the accumulator
var json = {
"market": [{
"company": [{
"name": "A"
},
{
"name": "B"
},
{
"name": "C"
}
]
},
{
"company": [{
"name": "D"
},
{
"name": "E"
}
]
}
]
}
var x = json.market.reduce(function(acc, curr) {
// acc mean the accumulator, 0 was passed as the first value
acc += curr.company.length;
return acc;
}, 0) // 0 is the initial value
console.log(x)
this code may be helpful.
var totallength=0;
json.market.reduce(function(i,o){totallength+=o.company.length;},0);
console.log(totallength)
I'm also posting here a solution using lodash, a bit more complicated, but generic. Which means it's gonna count the total elements of every property:
const data = {
market: [
{ company: [1, 2, 3], address: [1, 2, 4], test: [6,7]},
{ company: [4, 5, 6], address: [3, 4], bonus: [9] }
]
};
// get the length of every array (this can actually be done without lodash with Object.keys(obj).forEach...)
temp = data.market.map(obj => _.mapValues(obj, o => o.length));
counter = {}
temp.forEach(arr => Object.keys(arr).forEach(el => (counter[el] = (counter[el] || 0) + arr[el])))
// counter = { company: 6, address: 5, test: 2, bonus: 1 }
You can use Array.prototype.reduce() to get the result
Code:
const json = {
"market": [{
"company": [{
"name": "A"
},
{
"name": "B"
},
{
"name": "C"
}
]
},
{
"company": [{
"name": "D"
},
{
"name": "E"
}
]
}
]
};
const result = json.market.reduce((a, c) => a + c.company.length, 0);
console.log(result);