Using lodash, how could I group the names of people that have the same birthdates as seen below?
I wrote this out using nested for loops but was thinking there would be a more elegant way of doing this in lodash. I haven't used it much and trying to figure out which function to use.
[
{ "birthdate": "1993", "name": "Ben" },
{ "birthdate": "1994", "name": "John" },
{ "birthdate": "1995", "name": "Larry" },
{ "birthdate": "1995", "name": "Nicole" },
{ "birthdate": "1996", "name": "Jane" },
{ "birthdate": "1996", "name": "Janet" },
{ "birthdate": "1996", "name": "Dora" },
]
to
[
{ "birthdate": "1993", "names": [ "Ben" ] },
{ "birthdate": "1994", "names": [ "John"] },
{ "birthdate": "1995", "names": [ "Larry", "Nicole" ] },
{ "birthdate": "1996", "names": [ "Jane", "Janet", "Dora" ] }
]
You can use groupBy() to group each item in the collection by birthdate. After grouping the items, you can use map() to iterate each groups and form them into a new format that includes a birthdate and names. To acquire an array of names from the grouped items, you can use the map() function again to return all the name values.
var result = _(source)
.groupBy('birthdate')
.map(function(items, bdate) {
return {
birthdate: bdate,
names: _.map(items, 'name')
};
}).value();
var source = [
{ "birthdate": "1993", "name": "Ben" },
{ "birthdate": "1994", "name": "John" },
{ "birthdate": "1995", "name": "Larry" },
{ "birthdate": "1995", "name": "Nicole" },
{ "birthdate": "1996", "name": "Jane" },
{ "birthdate": "1996", "name": "Janet" },
{ "birthdate": "1996", "name": "Dora" },
];
var result = _(source)
.groupBy('birthdate')
.map(function(items, bdate) {
return {
birthdate: bdate,
names: _.map(items, 'name')
};
}).value();
document.body.innerHTML = '<pre>' + JSON.stringify(result, 0, 4) + '</pre>';
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
You can chain groupBy and map to achieve this.
_.chain(yourArray)
.groupBy('birthdate')
.toPairs()
.map(s=>{
return _.zipObject(['birthdate','names'],s)
})
.map(s=>{
s.names=_.map(s.names,x=>x.name);
return s;
})
.value()
You can use reduce() and find()
let arr = [{
"birthdate": "1993",
"name": "Ben"
},
{
"birthdate": "1994",
"name": "John"
},
{
"birthdate": "1995",
"name": "Larry"
},
{
"birthdate": "1995",
"name": "Nicole"
},
{
"birthdate": "1996",
"name": "Jane"
},
{
"birthdate": "1996",
"name": "Janet"
},
{
"birthdate": "1996",
"name": "Dora"
},
];
const res = arr.reduce((ac, a) => {
let temp = ac.find(x => x.birthdate === a.birthdate);
if (!temp) ac.push({ ...a,
name: [a.name]
})
else temp.name.push(a.name)
return ac;
}, [])
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
An simple and customizable pure javascript function to return an array of objects grouped by the attribute needed in the object
export const groupArrayBy = (arr, groupBy) => {
let newArr = []
arr.map((item) => {
if(item[groupBy]) {
let finded = newArr.filter((newItem) => newItem[groupBy] === item[groupBy])
if(finded.length > 0) {
finded[0].products.push(item)
} else {
newArr.push({category: item[groupBy], products: [item]})
}
}
})
return newArr
}
Lodash shortand
var data=[
{ "birthdate": "1993", "name": "Ben" },
{ "birthdate": "1994", "name": "John" },
{ "birthdate": "1995", "name": "Larry" },
{ "birthdate": "1995", "name": "Nicole" },
{ "birthdate": "1996", "name": "Jane" },
{ "birthdate": "1996", "name": "Janet" },
{ "birthdate": "1996", "name": "Dora" },
];
var list= _(data)
.groupBy('birthdate')
.map((items, birthdate)=> ({birthdate: birthdate,names: _.map(items, 'name')
})).value();
console.log(list)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
Related
I have products two types: simple and configurable:
"products" : [
{
"type": "simple",
"id": 1,
"sku": "s1",
"title": "Product 1",
"regular_price": {
"currency": "USD",
"value": 27.12
},
"image": "/images/1.png",
"brand": 9
},
{
"type": "configurable",
"id": 2,
"sku": "c1",
"title": "Product 2",
"regular_price": {
"currency": "USD",
"value": 54.21
},
"image": "/images/conf/default.png",
"configurable_options": [
{
"attribute_id": 93,
"attribute_code": "color",
"label": "Color",
"values": [
{
"label": "Red",
"value_index": 931,
"value": "#ff0000"
},
{
"label": "Blue",
"value_index": 932,
"value": "#0000ff"
},
{
"label": "Black",
"value_index": 933,
"value": "#000"
}
]
},
{
"attribute_code": "size",
"attribute_id": 144,
"position": 0,
"id": 2,
"label": "Size",
"values": [
{
"label": "M",
"value_index": 1441,
"value": 1
},
{
"label": "L",
"value_index": 1442,
"value": 2
}
]
}
],
"variants": [
{
"attributes": [
{
"code": "color",
"value_index": 931
},
{
"code": "size",
"value_index": 1441
}
],
"product": {
"id": 2001,
"sku": "c1-red-m",
"image": "/image/conf/red.png"
}
},
{
"attributes": [
{
"code": "color",
"value_index": 931
},
{
"code": "size",
"value_index": 1442
}
],
"product": {
"id": 2002,
"sku": "c1-red-l",
"image": "/image/conf/red.png"
}
},
{
"attributes": [
{
"code": "color",
"value_index": 932
},
{
"code": "size",
"value_index": 1441
}
],
"product": {
"id": 2003,
"sku": "c1-blue-m",
"image": "/image/conf/blue.png"
}
},
{
"attributes": [
{
"code": "color",
"value_index": 933
},
{
"code": "size",
"value_index": 1442
}
],
"product": {
"id": 2004,
"sku": "c1-black-l",
"image": "/image/conf/black.png"
}
}
],
"brand": 1
}
]
The above data I get with actions (Vuex)
GET_PRODUCTS_FROM_API({ commit }) {
return axios('http://localhost:8080/products', {
method: 'GET',
})
.then((products) => {
commit('SET_PRODUCTS_TO_STATE', products.data);
return products;
})
.catch((e) => {
console.log(e);
return e;
});
}
then I mutate the data:
SET_PRODUCTS_TO_STATE: (state, products) => {
state.products = products
}
and get from in getters
PRODUCTS(state) {
return state.products = state.products.map((product) => {
const brand = state.brands.find((b) => b.id === product.brand)
return {...product, brandName: brand?.title || 'no brand'}
})
}
after which i get the data in the component
At the moment I'm stuck on how to render the color and size attributes of a configurable product. Tell me how to do it right? Do I need to write logic in vuex or parent component?
I tried to push data from parent component to child. But it stopped there again.
I also tried to separate the color and size attributes separately using getters.
For Vuex, the syntax is the following
<template>
<div>
<div v-for="product in products" :key="product.id">
<span>type: {{ product.type }}</span>
<span>type: {{ product.title }}</span>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['products']),
...mapGetters('fancyNamespace', ['products']), // if namespaced
},
}
</script>
As of where to call it, directly into the component I guess. Otherwise, as explained here it may not be relevant to use Vuex at all.
PS: you can even rename on the fly if you want.
Solved this issue by Computed Properties and transfer props to child components
computed: {
getAttributeColors() {
let attributes_colors = []
this.product_data.configurable_options.map((item) => {
if(item.label === 'Color') {
attributes_colors.push(item.values)
}
})
return attributes_colors
},
getAttributeSize() {
let attributes_size = []
this.product_data.configurable_options.map((item) => {
if(item.label === 'Size') {
attributes_size.push(item.values)
}
})
return attributes_size
}
}
The task is to add a product with selection options to the cart. For example, take product with id:2, which has different color and size options. By clicking on the add product button, the product of the selected option should be added to the cart. Product options are predefined. You can see them in the products array below. At the moment, I have created a cart entity in the global store, where I can add a product without selection options and render it on the cart page.
"products" : [
{
"type": "simple",
"id": 1,
"sku": "s1",
"title": "Product 1",
"regular_price": {
"currency": "USD",
"value": 27.12
},
"image": "/images/1.png",
"brand": 9
},
{
"type": "configurable",
"id": 2,
"sku": "c1",
"title": "Product 2",
"regular_price": {
"currency": "USD",
"value": 54.21
},
"image": "/images/conf/default.png",
"configurable_options": [
{
"attribute_id": 93,
"attribute_code": "color",
"label": "Color",
"values": [
{
"label": "Red",
"value_index": 931,
"value": "#ff0000"
},
{
"label": "Blue",
"value_index": 932,
"value": "#0000ff"
},
{
"label": "Black",
"value_index": 933,
"value": "#000"
}
]
},
{
"attribute_code": "size",
"attribute_id": 144,
"position": 0,
"id": 2,
"label": "Size",
"values": [
{
"label": "M",
"value_index": 1441,
"value": 1
},
{
"label": "L",
"value_index": 1442,
"value": 2
}
]
}
],
"variants": [
{
"attributes": [
{
"code": "color",
"value_index": 931
},
{
"code": "size",
"value_index": 1441
}
],
"product": {
"id": 2001,
"sku": "c1-red-m",
"image": "/image/conf/red.png"
}
},
{
"attributes": [
{
"code": "color",
"value_index": 931
},
{
"code": "size",
"value_index": 1442
}
],
"product": {
"id": 2002,
"sku": "c1-red-l",
"image": "/image/conf/red.png"
}
},
{
"attributes": [
{
"code": "color",
"value_index": 932
},
{
"code": "size",
"value_index": 1441
}
],
"product": {
"id": 2003,
"sku": "c1-blue-m",
"image": "/image/conf/blue.png"
}
},
{
"attributes": [
{
"code": "color",
"value_index": 933
},
{
"code": "size",
"value_index": 1442
}
],
"product": {
"id": 2004,
"sku": "c1-black-l",
"image": "/image/conf/black.png"
}
}
],
"brand": 1
}]
My store:
import {createStore} from 'vuex'
import apiRequests from "#/store/actions/apiRequests";
import commonActions from './actions/actions'
import mutations from './mutations/mutations'
import getters from './getters/getters'
const actions = {...commonActions, ...apiRequests}
export default createStore({
state: {
products: [],
brands: [],
cart: []
},
mutations,
actions,
getters
})
actions:
export default {
ADD_TO_CART({commit}, product) {
commit('SET_CART', product)
},
INCREMENT_CART_ITEM({commit}, index) {
commit('INCREMENT', index)
},
DECREMENT_CART_ITEM({commit}, index) {
commit('DECREMENT', index)
},
DELETE_FROM_CART({commit}, index) {
commit('REMOVE_ITEM_FROM_CART', index)
}
}
mutations:
export default {
SET_PRODUCTS_TO_STATE: (state, products) => {
state.products = products
},
SET_BRANDS_TO_STATE: (state, brands) => {
const includesBrands = {}
for (const brand of brands) {
for (const product of state.products) {
if (product.brand === brand.id) {
includesBrands[brand.id] = brand
}
}
}
state.brands = Object.values(includesBrands)
state.brands.unshift({title: "All brands"})
},
SET_CART: (state, product) => {
if (state.cart.length) {
let isProductExists = false
state.cart.map((item) => {
if (item.id === product.id) {
isProductExists = true
item.quantity++
}
})
if (!isProductExists) {
state.cart.push({...product, quantity: 1})
}
} else {
state.cart.push({...product, quantity: 1})
}
},
REMOVE_ITEM_FROM_CART: (state, index) => {
state.cart.splice(index, 1)
},
INCREMENT: (state, index) => {
state.cart[index].quantity++
},
DECREMENT: (state, index) => {
if (state.cart[index].quantity > 1) {
state.cart[index].quantity--
}
}
}
and getters:
export default {
BRANDS(state) {
return state.brands
},
CART(state) {
return state.cart
},
PRODUCTS(state) {
return state.products = state.products.map((product) => {
const brand = state.brands.find((b) => b.id === product.brand)
return {...product, brandName: brand?.title || 'no brand'}
})
},
}
Now I am faced with the fact that I do not understand how I can add a certain product variant to the cart and draw it on the cart page.
I need to do a conditional filtering using Mule 3. A sample payload is given below:
{
"MainNode": {
"ID": "123",
"MainNodekey1": "1",
"sysDetail": [
{
"sysDetail11": "localhost1",
"sysDetail21": "443",
"country": [
{
"k1": "country",
"value": [
"IN"
]
}
]
},
{
"sysDetail21": "localhost2",
"sysDetail22": "443",
"country": [
{
"k2": "country",
"value": [
"RSA",
"UK",
"SL"
]
}
]
}
],
"sysDetai2": {},
"sysDetai3": {}
},
"MainCode": "AAA",
"MainTyoe": "BBB",
"MyArray": [
{
"ObjId": "100",
"Obj1": {
"SubObj11": {},
"SubObj12": {},
"ObjCountry": "IN",
"ObjId": "A100"
},
"Obj1Source": {}
},
{
"ObjId": "200",
"Obj2": {
"SubObj21": {},
"SubObj22": {},
"ObjCountry": "IN",
"ObjId": "B100"
},
"Obj2Source": {}
}
]
}
The output that I need is to filter the main payload by applying a filter like below:
payload.MainNode.sysDetail.country..value == payload.Obj1.ObjCountry (which will be constant for all array elements)
Expected output is below:
{
"MainNode": {
"ID": "123",
"MainNodekey1": "1",
"sysDetail": [
{
"sysDetail11": "localhost1",
"sysDetail21": "443",
"country": [
{
"k1": "country",
"value": [
"IN"
]
}
]
}
],
"sysDetai2": {},
"sysDetai3": {}
},
"MainCode": "AAA",
"MainTyoe": "BBB",
"MyArray": [
{
"ObjId": "100",
"Obj1": {
"SubObj11": {},
"SubObj12": {},
"ObjCountry": "IN",
"ObjId": "A100"
},
"Obj1Source": {}
},
{
"ObjId": "200",
"Obj2": {
"SubObj21": {},
"SubObj22": {},
"ObjCountry": "IN",
"ObjId": "B100"
},
"Obj2Source": {}
}
]
}
I have a list of products in my collection like this:
[
{
"name": "product1",
"categories": [
{
"category": "Food",
"subcategory": "Nuts"
},
{
"category": "Candies",
"subcategory": "Chocolate"
}
]
},
{
"name": "product2",
"categories": [
{
"category": "Food",
"subcategory": "Nuts"
},
{
"category": "Candies",
"subcategory": "Mint"
}
]
},
{
"name": "product3",
"categories": [
{
"category": "Food",
"subcategory": "Cakes"
},
{
"category": "Pastries",
"subcategory": "Chocolate"
}
]
}
]
I want to get a count of products using aggregate. If I run
db.collection.aggregate([{ $group: { _id: "$categories", count: { $sum: 1 }} }])
I get this:
[
{
"_id": [{ "category": "Food", "subcategory": "Nuts" }, { "category": "Candies", "subcategory": "Chocolate" }],
"count": 1
},
{
"_id": [{ "category": "Food", "subcategory": "Nuts" }, { "category": "Candies", "subcategory": "Mint" }],
"count": 1
},
{
"_id": [{ "category": "Food", "subcategory": "Cakes" }, { "category": "Pastries", "subcategory": "Chocolate" }],
"count": 1
}
]
But what I really need is to find count per category and subcategory, like this:
[
{ "_id": { "category": "Food", "subcategory": "Nuts" }, "count": 2 },
{ "_id": { "category": "Food", "subcategory": "Cakes" }, "count": 1 },
{ "_id": { "category": "Candies", "subcategory": "Chocolate" }, "count": 1 },
{ "_id": { "category": "Candies", "subcategory": "Mint" }, "count": 1 },
{ "_id": { "category": "Pastries", "subcategory": "Chocolate" }, "count": 1 }
]
Is there a query to obtain this?
You have to $unwind categories first
db.collection.aggregate([
{
$unwind: "$categories"
},
{
$group: { _id: "$categories", count: { $sum: 1 } }
}
])
Problem with using mapObject function properly.
Trying to retain existing array structure but calculate number of vehicles and properties and update the existing array that contains the value.
GENERAL data comes from one source, VEHICLE data comes from another source, PROPERTY data comes from another source. So when merging, I have to update GENERAL data with count of other source data.
Also GENERAL is an array object, it will always have 1. So using GENERAL[0] is safe and fine.
Original Payload
[
{
"commId": "1",
"GENERAL": [
{
"ID": "G1",
"VEHICLE_COUNT": "TODO",
"PROPERTY_COUNT": "TODO"
}
],
"VEHICLE": [
{
"ID": "V1-1"
},
{
"ID": "V1-2"
}
],
"PROPERTY": [
{
"ID": "P1-1"
}
]
},
{
"commId": "2",
"GENERAL": [
{
"ID": "G2",
"VEHICLE_COUNT": "TODO",
"PROPERTY_COUNT": "TODO"
}
],
"VEHICLE": [
{
"ID": "V2-1"
}
],
"PROPERTY": [
{
"ID": "P2-1"
},
{
"ID": "P2-2"
}
]
},
{
"commId": "3",
"GENERAL": [
{
"ID": "G3",
"VEHICLE_COUNT": "TODO",
"PROPERTY_COUNT": "TODO"
}
],
"VEHICLE": [
{
"ID": "V3-1"
},
{
"ID": "V3-2"
},
{
"ID": "V3-3"
}
]
}
]
Tried using map to loop through the payload and tried modifying 2 attribute but only managed to map one but even that is showing wrong output.
test map (item, index) -> {
(item.GENERAL[0] mapObject (value, key) -> {
(key): (value == sizeOf (item.VEHICLE)
when (key as :string) == "VEHICLE_COUNT"
otherwise value)
})
}
Expected output:
[
{
"commId": "1",
"GENERAL": [
{
"ID": "G1",
"VEHICLE_COUNT": "2",
"PROPERTY_COUNT": "1"
}
],
"VEHICLE": [
{
"ID": "V1-1"
},
{
"ID": "V1-2"
}
],
"PROPERTY": [
{
"ID": "P1-1"
}
]
},
{
"commId": "2",
"GENERAL": [
{
"ID": "G2",
"VEHICLE_COUNT": "1",
"PROPERTY_COUNT": "2"
}
],
"VEHICLE": [
{
"ID": "V2-1"
}
],
"PROPERTY": [
{
"ID": "P2-1"
},
{
"ID": "P2-2"
}
]
},
{
"commId": "3",
"GENERAL": [
{
"ID": "G3",
"VEHICLE_COUNT": "3",
"PROPERTY_COUNT": "0"
}
],
"VEHICLE": [
{
"ID": "V3-1"
},
{
"ID": "V3-2"
},
{
"ID": "V3-3"
}
]
}
]
Getting totally wrong output so far:
[
{
"ID": "G1",
"VEHICLE_COUNT": false,
"PROPERTY_COUNT": "TODO"
},
{
"ID": "G2",
"VEHICLE_COUNT": false,
"PROPERTY_COUNT": "TODO"
},
{
"ID": "G3",
"VEHICLE_COUNT": false,
"PROPERTY_COUNT": "TODO"
}
]
Edited: Update for dynamic transform
The below dataweave transform is not particularly attractive, but it might work for you.
Thanks to Christian Chibana for helping me find a dynmaic answer by answering this question: Why does Mule DataWeave array map strip top level objects?
%dw 1.0
%output application/json
---
payload map ((item) ->
(item - "GENERAL") ++
GENERAL: item.GENERAL map (
$ - "VEHICLE_COUNT"
- "PROPERTY_COUNT"
++ { VEHICLE_COUNT: sizeOf (item.VEHICLE default []) }
++ { PROPERTY_COUNT: sizeOf (item.PROPERTY default []) }
)
)
It is dynamic, so everything should be copied across as it comes in, with only the two fields you want being updated.
The output for this transform with the input you supplied is below. Only difference from your desired is that the counts are shown as numbers rather than strings. If you really need them as strings you can cast them like (sizeOf (comm.VEHICLE default [])) as :string,
[
{
"commId": "1",
"VEHICLE": [
{
"ID": "V1-1"
},
{
"ID": "V1-2"
}
],
"PROPERTY": [
{
"ID": "P1-1"
}
],
"GENERAL": [
{
"ID": "G1",
"VEHICLE_COUNT": 2,
"PROPERTY_COUNT": 1
}
]
},
{
"commId": "2",
"VEHICLE": [
{
"ID": "V2-1"
}
],
"PROPERTY": [
{
"ID": "P2-1"
},
{
"ID": "P2-2"
}
],
"GENERAL": [
{
"ID": "G2",
"VEHICLE_COUNT": 1,
"PROPERTY_COUNT": 2
}
]
},
{
"commId": "3",
"VEHICLE": [
{
"ID": "V3-1"
},
{
"ID": "V3-2"
},
{
"ID": "V3-3"
}
],
"GENERAL": [
{
"ID": "G3",
"VEHICLE_COUNT": 3,
"PROPERTY_COUNT": 0
}
]
}
]