Related
For one of my e-commerce application requirement, I have a nested array of the form (Sample):
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
]
To this array, I need to insert new objects to the array 'labels' if the group value equals 'upper-wear'.
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
]
This is what I tried so far, considering that for now I only need to insert to single label (i.e. 'upper-wear') (in future, there can be multiple labels category 'upper-wear', 'bottom-wear', to be inserted into):
const updatedArray = data.map((datum) => {
if (datum.group === 'upper-wear') {
return {
...datum,
labels: [...datum.labels, ...newDataToInsert]
};
}
});
console.log(updatedArray);
But there seems to be a silly issue that I am missing as the result returns like this:
[
{
id: 1,
group: 'upper-wear',
labels: [ [Object], [Object] ],
popular: true
},
undefined
]
I know there may be better approaches available, but this is what I can think of as the minimum solution for now.
any help to resolve the current or any better solution will be highly appreciated.
Try with this
updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
})
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
];
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
];
const updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
});
console.log(updatedArray)
Explaination
Here while mapping the data, we check for the condition
IF
If it matches then we will first copy the whole object from the variable b return { ...b }
after that we take another variable with the same name lables return { ...d, labels: d.labels.concat(newDataToInsert) },As per the JSON default nature the new variable with the same name will hold the latest value
Here in labels we first take a copy of old data and then merge it with newDataToInsert array labels: d.labels.concat(newDataToInsert), It will merge 2 arrays and store them in JSON with the name labels
Else
In else we just return the current values else { return d; }
You don't actually need to iterate with map over the array. Just find an object in the array and change what you want.
const data=[{id:1,group:"upper-wear",labels:[{type:"shirts",quantity:"20"}],popular:true},{id:2,group:"bottom-wear",lables:[{type:"trousers",quantity:"31"}],popular:true}];
const newDataToInsert=[{type:"blazers",quantity:19}];
data.find(({ group }) => group === 'upper-wear')?.labels.push(...newDataToInsert);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You're not returning all objects from your map. you're only returning a result when your criteria is met. This is resulting in your undefined objects...
const data = [
{ "id": 1, "group": "upper-wear", "labels": [ { "type": "shirts", "quantity": "20", }, ], popular: true },
{ "id": 2, "group": "bottom-wear", "lables": [ { "type": "trousers", "quantity": "31", }, ], popular: true },
]
const newDataToInsert = [ { "type": 'blazers',"quantity": 19 }, ]
const updatedArray = data.map(datum => {
if (datum.group === 'upper-wear') datum.labels = [...datum.labels, ...newDataToInsert]
return datum
});
console.log(updatedArray);
You can use Array#find to locate the desired group and then change labels for the group found. There are two options depending on how many items you would like to insert. Use Array#push to add the desired item; use forEach for more than one item:
const searchgroup = "upper-wear";
const target = data.find(({group}) => group === searchgroup);
target.labels.push(...newDataToInsert); //For one item to insert
//newDataToInsert.forEach(label => target.labels.push( label )); //For more than one item
const data = [{"id": 1, "group": "upper-wear", "labels": [{"type": "shirts", "quantity": "20"},],popular: true }, {"id": 2, "group": "bottom-wear", "lables": [{"type": "trousers", "quantity": "31", },],popular: true}];
const newDataToInsert = [{"type": 'blazers', "quantity": 19}];
//group to find
const searchgroup = "upper-wear";
//target element in data
const target = data.find(({group}) => group === searchgroup);
//check if group was found
if( target ) {
//if there's only one product in newDataToInsert us this:
//target.labels.push(...newDataToInsert);
//if you have more than one product to be inserted use this; also works for one
newDataToInsert.forEach(label => target.labels.push( label ));
} else {
console.log( `No such group found: ${searchgroup}!` );
}
console.log( data );
I get an input like this:
input 1:
{
"name": "Ben",
"description": "Ben",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
input 2
{
"name": "Ice",
"description": "Ice",
"attributes": [
{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
input 3
{
"name": "Itay",
"description": "Itay",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
What I want to do is count the amount of each type of background and each type of hair-color appearing.
(These are sample examples and in reality there are more types and different values)
Let's say in these examples we have 2 objects that have a background as default then I want to have a count of that like so:
export interface TraitCount {
value: string,
count: number
}
export interface CountOfEachAttribute {
trait_type: string,
trait_count: traitCount[] | null,
total_variations: number
}
I want the most effective code because there are other aspects to the code, in addition it will run on 5-10k queries not just three, so needs
to run in good times too :D
(It's similar to my other question done with python but now I need it in js also)
Atm it's something like this:
(Apart of a much bigger code so keep that in mind)
setInitalCountOfAllAttribute( state, { payload }: PayloadAction<CountOfEachAttribute[] | null> ) {
if (payload === null) {
state.countOfAllAttribute = null;
} else {
state.countOfAllAttribute = payload;
}
},
setCountOfAllAttribute(state, { payload }: PayloadAction<Attribute>) {
if (state.countOfAllAttribute !== null) {
state.countOfAllAttribute.map(
(countOfEachAttribute: CountOfEachAttribute) => {
// Find the trait type
if (countOfEachAttribute.trait_type === payload.trait_type) {
// initiate the trait count array to store all the trait values and add first trait value
if (countOfEachAttribute.trait_count === null) {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [new_trait_count];
countOfEachAttribute.total_variations++;
}
// Trait array already existed.
else {
// Check if value already present or not
const checkValue = (obj: any) => obj.value === String(payload.value);
const isPresent = countOfEachAttribute.trait_count.some(checkValue)
const isPresent2 = countOfEachAttribute.trait_count.find((elem: any) => elem.value === String(payload.value))
// Value matched, increase its count by one
if (isPresent2) {
countOfEachAttribute.trait_count &&
countOfEachAttribute.trait_count.map((trait) => {
if (trait.value === payload.value) {
trait.count++;
}
});
}
// Value doesn't match, add a new entry and increase the count of variations by one
else {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [
...countOfEachAttribute.trait_count,
new_trait_count,
];
countOfEachAttribute.total_variations++;
}
}
}
}
);
}
},
You can merge all arrays and use Array.reduce.
const input1 = {
"name": "Ben",
"description": "Ben",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const input2 = {
"name": "Ice",
"description": "Ice",
"attributes": [{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
const input3 = {
"name": "Itay",
"description": "Itay",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const mergedInput = [input1, input2, input3];
const result = mergedInput.reduce((acc, item) => {
item.attributes.forEach(attrItem => {
const existType = acc.find(e => e.trait_type == attrItem.type);
if (existType) {
var existAttr = existType.trait_count.find(e => e.value == attrItem.value);
if (existAttr) {
existAttr.count++;
} else {
existType.trait_count.push({
value: attrItem.value,
count: 1
});
existType.total_variations++;
}
} else {
acc.push({
trait_type: attrItem.type,
trait_count: [{
value: attrItem.value,
count: 1
}],
total_variations: 1
})
}
});
return acc;
}, []);
console.log(result);
I suggest instead of creating an array for trait_count to make it an object so you don't have to iterate over it whenever you are adding a new attribute. In the snippet below I'm using the value of the attribute as a sort of hash that allows the access to the given property without having to call the Array.prototype.find function
const input1 = {"name":"Ben","description":"Ben","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
const input2 = {"name":"Ice","description":"Ice","attributes":[{"type":"Background","value":"Green"},{"type":"Hair-color","value":"White"}]};
const input3 = {"name":"Itay","description":"Itay","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
function countAtributes(input, totalCounts={}) {
input.attributes.forEach((attribute) => {
if (!totalCounts[attribute.type])
totalCounts[attribute.type] = {trait_type: attribute.type, trait_count: {}, total_variations: 0};
if (!totalCounts[attribute.type].trait_count[attribute.value]) {
totalCounts[attribute.type].trait_count[attribute.value] = {value: attribute.value, count: 1};
totalCounts[attribute.type].total_variations+=1;
}
else totalCounts[attribute.type].trait_count[attribute.value].count +=1;
})
}
const totalCounts = {};
countAtributes(input1, totalCounts);
countAtributes(input2, totalCounts);
countAtributes(input3, totalCounts);
console.log(totalCounts);
It could be turned into the array afterwards with Object.values if necessary
I believe it is a much better approach to what you had before as you don't have to iterate over the tables of trait_counts. In theory it should significantly reduce the time taken. Iterating over the array and checking a condition each time is much slower than key lookup in Javascript object
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I am trying to replace name, occupation and title for the following array of objects and capitalise each value. Some of the values can be null and if the value is null then replace null with "N/A". What would be the shortest/best way in es6 without all the if/else statements?
const array = [{
"id": 1,
"name": "sarah",
"title": "miss,
"occupation": "student"
}, {
"id": 2,
"name": null,
"title" : null,
"occupation": null,
}]
What I have so far:
const result = array.map((x) => {
if (x.name){
x.name = x.name.charAt(0).toUpperCase() + x.name.slice(1)
} else {
x.name = "N/A"
}
if (x.title){
x.title = x.title.charAt(0).toUpperCase() + x.title.slice(1)
} else {
x.title = "N/A"
}
if (x.occupation){
x.occupation = x.occupation.charAt(0).toUpperCase() + x.occupation.slice(1)
} else {
x.occupation = "N/A"
}
return x
});
Expected output:
const array = [{
"id": 1,
"name": "Sarah",
"title": "Miss,
"occupation": "Student"
}, {
"id": 2,
"name": "N/A",
"title" : "N/A",
"occupation": "N/A",
}]
You can just create a function which does the repetitive work
const array = [{
"id": 1,
"name": "sarah",
"title": "miss",
"occupation": "student"
}, {
"id": 2,
"name": null,
"title": null,
"occupation": null,
}]
function assignValue(a) {
return a ? a.charAt(0).toUpperCase() + a.slice(1) : "N/A"
}
const newData = array.map(a => {
return {
id: a.id,
name: assignValue(a.name),
title: assignValue(a.title),
occupation: assignValue(a.occupation)
}
});
console.log(newData)
const output = array.map(object => {
return Object.fromEntries(Object.entries(object).map(entry => {
if(entry[0] == "id") return [entry[0], entry[1]]
const newValue = entry[1] ? entry[1].charAt(0).toUpperCase() + entry[1].slice(1) : "N/A"
return [entry[0], newValue]
}));
});
This uses ES8 features.
You can make it more generic for any string value as part of your object definition
const array = [{
id: 1,
name: "first middle last",
title: "miss",
occupation: "software engineer"
},
{
id: 2,
name: null,
title: null,
occupation: "student"
}
];
capitalize = (str) => {
return str
.toLowerCase()
.split(" ")
.map((wrd) => {
return wrd[0].toUpperCase() + wrd.slice(1);
})
.join(" ");
};
const transformArr = array.map((el) => {
for (const key of Object.keys(el)) {
if (!el[key]) {
el[key] = "N/A";
} else if (typeof el[key] === "string") {
el[key] = capitalize(el[key]);
}
}
return el;
});
console.log(transformArr);
You can use a small little function to avoid all the duplicate code:
function leadingUpper(str) {
if (str) {
return str.charAt(0).toUpperCase() + str.slice(1);
} else {
return "N/A";
}
}
Then, if you can modify the array in place, you won't need to create a new duplicate array:
for (const obj of array) {
for (const field of ["name", "title", "occupation"]) {
array[field] = leadingUpper(array[field]);
}
}
Or, if you want to create the new array, rather than modifying the existing one:
const result = array.map(obj => {
for (const field of ["name", "title", "occupation"]) {
array[field] = leadingUpper(array[field]);
}
});
Or, you could even embed the leadingUpper() function inline if you want:
const result = array.map(obj => {
for (const field of ["name", "title", "occupation"]) {
const str = array[field];
array[field] = str ? str.charAt(0).toUpperCase() + str.slice(1) : "N/A";
}
});
Note: Unlike some of the other solutions offered, this is "safe" in that it only modifies the specifically named properties name, title and occupation. If there happen to be other properties on any of the objects, it won't modify them. I consider this a good, defensive coding practice that isn't brittle if some other developer adds a new property to the object in the future that isn't supposed to get the capitalization treatment.
I have the array of objects as below. I want to loop through it and get Closed property values. It should be a concatenation of all the values found in each object.
For e.g. in below case, i want final result as 121212 since it has 12 in all the 3 objects.
const data = [
{
"personId": "1208007855",
"details": {
"Closed": "12"
}
},
{
"personId": "1559363884",
"details": {
"Closed": "12"
}
},
{
"personId": "973567318",
"details": {
"Closed": "12"
}
}
]
can someone let me know how to achieve this. I tried this way but couldnt succeed in achieving the result. I could only get the value of first object. Not sure how i can concatenate and store next values in this loop
There might be a situation where some objects might not have Closed property.
const totalClosed = data.forEach(function (arrayItem) {
const x = arrayItem.details.Closed;
console.log(x);
});
Try the following:
const data = [
{
"personId": "1208007855",
"details": {
"Closed": "12"
}
},
{
"personId": "1559363884",
"details": {
"Closed": "12"
}
},
{
"personId": "973567318",
"details": {
"Closed": "12"
}
}
];
result = '';
for (let i in data) {
result += data[i].details.Closed
}
You can use the .reduce function:
data.reduce((accumulator, item) => accumulator += item.details.Closed, '')
=> 121212
Using foreach exactly the same way you were trying:
const data = [
{
personId: '1208007855',
details: {
Closed: '12'
}
},
{
personId: '1559363884',
details: {
Closed: '12'
}
},
{
personId: '973567318',
details: {
Closed: '12'
}
}
];
let totalClosed = '';
data.forEach(function (arrayItem) {
totalClosed = totalClosed + arrayItem.details.Closed;
});
console.log(totalClosed);
In functional way, using reduce
const data = [
{
"personId": "1208007855",
"details": {
"Closed": "12",
"Analyze": "10"
}
},
{
"personId": "1559363884",
"details": {
"Closed": "12",
"Analyze": "10"
}
},
{
"personId": "973567318",
"details": {
"Closed": "12",
"Analyze": "10"
}
}
]
const { Closed, Analyze } = data.reduce((acc, cur) => {
acc.Closed += cur?.details?.Closed ?? ''
acc.Analyze += cur?.details?.Analyze ?? ''
return acc
}, { Closed: "", Analyze: "" })
console.log({ Closed, Analyze })
let str = "";
for(let i=0; i<data.length;i++){
str+= data[i].details.Closed;
}
console.log(str);
Also, with forEach, the elements might not be processed in the same order (0 to n) and you may find different results than you expect.
If you want a string and have an array, the best method is reduce:
const totalClosed = data.reduce(function (accumulator, currentVal) {
const closed = currentVal.details.Closed || '';
return accumulator + closed;
}, '');
let str = ''
const totalClosed = data.forEach(function (arrayItem) {
if(arrayItem.details.Closed){
str += arrayItem.details.Closed;
}
});
console.log(str)
You can create an empty string and add to it if the closed field exists, if there is another condition in 'Closed' you can check there in the if statement.
You can reduce the data entries by destructuring the entry in the reducer and concatenating the Closed value to the running res (result). You can use the nullish-coalescing operator (??) to use an empty string instead of undefined when concatenating.
const data = [
{ "personId": "1208007855" , "details": { "Closed": "12" } },
{ "personId": "1559363884" , "details": { "Closed": "12" } },
{ "personId": "0000000000" , "details": { "Open" : "8" } }, // New!
{ "personId": "973567318" , "details": { "Closed": "12" } }
];
const value = data.reduce((res, { details: { Closed } }) => res + (Closed ?? ''), '');
console.log(value);
If you want to implement using loops Try Using:
const data = [
{
"personId": "1208007855",
"details": {
"Closed": "12"
}
},
{
"personId": "1559363884",
"details": {
"Closed": "12"
}
},
{
"personId": "973567318",
"details": {
"Closed": "12"
}
}
];
var res= ''
data.forEach((item)=>{if(item.details.Closed){ res += item.details.Closed;}})
console.log(res)
And this can also be done by using higher order functions:
Try using :
data.reduce((res, item) =>{if(item.details.Closed)
res += item.details.Closed;
return res}, '')
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I would like to convert my below object to a specific format
Product: { "alias":"d8156ad9-06af-45ca-a8eb-17f6148f1bd2",
"manufacturing_company":{
"alias":"1",
"name":"Company",
"status":1
},
"form":{
"alias":"2",
"name":"Suppository",
"status":1
},
"generic":{
"alias":"1",
"name":"Esomeprazole",
"status":1
},
"name":"AA",
"trading_price":15,
"purchase_price":10,
"pack_size":"10",
"is_salesable":true,
"primary_unit":{
"alias":"1",
"name":"Form1",
"status":2
},
"secondary_unit":{
"alias":"1",
"name":"Form1",
"status":2
},
"conversion_factor":25,
"code":"201",
"species":"drfgfgd",
"strength":"96",
"full_name":"AA 96",
"status":1,
"category":{
"alias":"8",
"name":"Test2",
"status":1
},
"subgroup":{
"alias":"4",
"name":"CDCD",
"status":1,
"product_group":{
"id":9,
"alias":"1",
"name":"zsdfasd",
"status":1
}
}
}
to
Product: { "alias":"d8156ad9-06af-45ca-a8eb-17f6148f1bd2",
"manufacturing_company":"1",
"form":"2",
"generic":"1",
"name":"AA",
"trading_price":15,
"purchase_price":10,
"pack_size":"10",
"is_salesable":true,
"primary_unit":"1",
"secondary_unit":"1",
"conversion_factor":25,
"code":"201",
"species":"drfgfgd",
"strength":"96",
"full_name":"AA 96",
"status":1,
"category":"8",
"subgroup":"4"
}
Loop through the object and create a new one based on whether the values are scalar or not:
let obj={Product:{alias:"d8156ad9-06af-45ca-a8eb-17f6148f1bd2",manufacturing_company:{alias:"1",name:"Company",status:1},form:{alias:"2",name:"Suppository",status:1},generic:{alias:"1",name:"Esomeprazole",status:1},name:"AA",trading_price:15,purchase_price:10,pack_size:"10",is_salesable:!0,primary_unit:{alias:"1",name:"Form1",status:2},secondary_unit:{alias:"1",name:"Form1",status:2},conversion_factor:25,code:"201",species:"drfgfgd",strength:"96",full_name:"AA 96",status:1,category:{alias:"8",name:"Test2",status:1},subgroup:{alias:"4",name:"CDCD",status:1,product_group:{id:9,alias:"1",name:"zsdfasd",status:1}}}};
let resObj = {}
Object.entries(obj.Product).map(([key, value]) => {
if(typeof value === "object"){
resObj[key] = value.alias
}else{
resObj[key] = value
}
})
console.log(resObj)
You could take for object alias or the value for creating a new object.
var data = { alias: "d8156ad9-06af-45ca-a8eb-17f6148f1bd2", manufacturing_company: { alias: "1", name: "Company", status: 1 }, form: { alias: "2", name: "Suppository", status: 1 }, generic: { alias: "1", name: "Esomeprazole", status: 1 }, name: "AA", trading_price: 15, purchase_price: 10, pack_size: "10", is_salesable: true, primary_unit: { alias: "1", name: "Form1", status: 2 }, secondary_unit: { alias: "1", name: "Form1", status: 2 }, conversion_factor: 25, code: "201", species: "drfgfgd", strength: "96", full_name: "AA 96", status: 1, category: { alias: "8", name: "Test2", status: 1 }, subgroup: { alias: "4", name: "CDCD", status: 1, product_group: { id: 9, alias: "1", name: "zsdfasd", status: 1 } } },
result = Object.fromEntries(Object
.entries(data)
.map(([k, v]) => [k, v && typeof v === 'object'
? v.alias
: v]
)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If data is the json you want to convert , then use the below code to get the desired result.
Object.keys(data).reduce((result , current)=>{
var value = "";
if(typeof(data[current]) == "object")
{
value = data[current]["alias"]
}
else{
value = data[current]
}
var d = {}
d[current] = value;
Object.assign(result,d);
return result;
},{})