3

I have the following object:

const attributes = [
    {
        id: "a",
        values: [
            {
                value: [
                    {
                        _id: "aa",
                        value: "1500"
                    },
                    {
                        _id: "ab",
                        value: "580"
                    }
                ]
            },
            {
                value: [
                    {
                        _id: "aa",
                        value: "400"
                    }
                ]
            }
        ]
    },
    {
        id: "a",
        values: [
            {
                value: [
                    {
                        _id: "aa",
                        value: "420"
                    },
                    {
                        _id: "ab",
                        value: "300"
                    }
                ]
            },
            {
                value: [
                    {
                        _id: "aa",
                        value: "480"
                    },
                    {
                        _id: "ab",
                        value: "1000"
                    }
                ]
            },
            {
                value: [
                    {
                        _id: "aa",
                        value: "880"
                    },
                    {
                        _id: "ab",
                        value: "740"
                    }
                ]
            }
        ]
    },
    {
        id: "b",
        values: [
            {
                value: [
                    {
                        _id: "ba",
                        value: "1500"
                    },
                    {
                        _id: "bb",
                        value: "580"
                    }
                ]
            },
            {
                value: [
                    {
                        _id: "ba",
                        value: "400"
                    }
                ]
            }
        ]
    },
];

I want to group the data based on attribute id and value _id, so I can have the following object in return:

[
    {
        id: "a",
        values: [
            {
                value: [
                    {
                        _id: "aa",
                        values: ["1900", "1780"]
                    },
                    {
                        _id: "ab",
                        values: ["580", "2040"]
                    }
                ]
            }
        ]
    },
    {
        id: "b",
        values: [
            {
                value: [
                    {
                        _id: "ba",
                        values: ["1900"]
                    },
                    {
                        _id: "bb",
                        values: "[580"]
                    }
                ]
            },
        ]
    },
];

If the result doesn't make sense I can explain more. Anyway, I tried to attempt the result by doing the following but it doesn't work as expected and I'm sure there's a cleaner way to do so.

let newAttributes = [];
attributes.forEach(attribute => {
    let currentAttribute = { id: attribute.id, values: attribute.values[0] };
    attribute.values.shift();
    attribute.values.forEach(attributeValues => {
        attributeValues.value.forEach(vl => {
            var values = currentAttribute.values.value.find(vl2 => vl2._id === vl._id);
            if (values && attribute.id === currentAttribute.id) {
                if (!values.values) {
                    values.values = [];
                }
                values.values.push(vl.value);
            }
        });
    });
    newAttributes.push(attributes);
});

newAttributes.forEach(attribute => {
    attribute.values.value.forEach(vl => {
        if (vl.values) {
            vl.values.push(vl.value);
        } else {
            vl.values = [vl.value]
        }
        delete vl.value;
    });
    attribute.values.value.forEach(vl => {
        vl.values = vl.values.reduce((a, b) => {
            return Number(a) + Number(b);
        }, 0);
         vl.values =  [vl.values.toString()]
    });
});

console.log(newAttributes);

EDIT:

Explanation: let's take an example for the attribute id = 'a' and value _id = 'aa':

For the first attribute with id = 'a', we have two values with _id = 'aa', one has 1500 and the other has 400, we do the sum of those two values we got 1900, then we have another attribute with id = 'a' with 3 values with _id = 'aa', with values 420, 480 and 880, the sum of those values is 1780, we push that sum in the values with _id = 'aa' of the firt attribute with id = 'a'

2
  • expected output is not clea Commented May 16, 2020 at 4:45
  • @brk See my edit please, I added an explanation. Commented May 16, 2020 at 4:57

3 Answers 3

2

const attributes = [
    {
        id: "a",
        values: [
            {
                value: [
                    {
                        _id: "aa",
                        value: "1500"
                    },
                    {
                        _id: "ab",
                        value: "580"
                    }
                ]
            },
            {
                value: [
                    {
                        _id: "aa",
                        value: "400"
                    }
                ]
            }
        ]
    },
    {
        id: "a",
        values: [
            {
                value: [
                    {
                        _id: "aa",
                        value: "420"
                    },
                    {
                        _id: "ab",
                        value: "300"
                    }
                ]
            },
            {
                value: [
                    {
                        _id: "aa",
                        value: "480"
                    },
                    {
                        _id: "ab",
                        value: "1000"
                    }
                ]
            },
            {
                value: [
                    {
                        _id: "aa",
                        value: "880"
                    },
                    {
                        _id: "ab",
                        value: "740"
                    }
                ]
            }
        ]
    },
    {
        id: "b",
        values: [
            {
                value: [
                    {
                        _id: "ba",
                        value: "1500"
                    },
                    {
                        _id: "bb",
                        value: "580"
                    }
                ]
            },
            {
                value: [
                    {
                        _id: "ba",
                        value: "400"
                    }
                ]
            }
        ]
    },
];

let newStructure = 
	attributes.map(attr => {
		let tempValues = {};
		attr.values.forEach((value, index)=> { 
			value.value.forEach((v)=>{
				if(typeof tempValues[v._id] == "undefined")
					tempValues[v._id] = 0;
				tempValues[v._id] += +v.value

			})
		})
		return {
			id: attr.id,
			values: tempValues
		}

}).reduce((accumulator, currentValue)=>{
	if(typeof accumulator[currentValue.id] == "undefined")
		accumulator[currentValue.id] = { id: currentValue.id, values: {} };
	Object.keys(currentValue.values).forEach( valueKey =>{
		if(typeof accumulator[currentValue.id].values[valueKey] == "undefined")
				accumulator[currentValue.id].values[valueKey] = [currentValue.values[valueKey]];
		else
				accumulator[currentValue.id].values[valueKey].push(currentValue.values[valueKey]);
	})
	
	return accumulator
},{})

newStructure = Object.keys(newStructure).map(itemKey => {
	return {
		id: itemKey,
		values: {
			value: Object.keys(newStructure[itemKey].values).map(valueKey => {
				return  {
					_id: valueKey,
					value: newStructure[itemKey].values[valueKey]
				}
			})
		}
	}
});

console.log(newStructure)

Sign up to request clarification or add additional context in comments.

Comments

1

Using more direct/native javascript object forms, I was able to make most parts more discrete. This code usually assumes that an object might not have an initial value for a given index, so doesn't perform any optimizations based on that which you appear to have with attribute.values[0] initialization and future assumption that only those indices are used.

let newAttributes = {}; // will be converted back
for (attribute of attributes){

    let newValues = []; // turn it into [ { "aa":1500, "ab":580 }, {"aa":400} ]
    for (valueSet of attribute.values){
        let newObj = {};
        for (value of valueSet.value){
            newObj[value._id] = Number(value.value);
        }
        newValues.push(newObj);
    }

    let sum = {};
    for (value of newValues){
        for (id in value){
            if (!(id in sum)) sum[id] = 0;
            sum[id] += value[id];
        }
    }

    if ( !(attribute.id in newAttributes))
        newAttributes[attribute.id] = {};

    outAttrib = newAttributes[attribute.id]
    for (id in sum){
        if ( !(id in outAttrib)) outAttrib[id] = [];
        outAttrib[id].push(sum[id].toString());
    }

}
// at this point, the object would be in, imo, more manageable form
// a: { aa: [ '1900', '1780' ], ab: [ '580', '2040' ] },
// b: { ba: [ '1900' ], bb: [ '580' ] }

let out = [];
for (id in newAttributes){ // can be integrated into former loop but I think this makes more sense
    let value = [];
    for (_id in newAttributes[id]){
        value.push({_id: _id, values: newAttributes[id][_id]});
    }
    out.push({id: id, values: [ { value: value } ] });
}
console.log(out);

Comments

1

I write the code from scratch, it's working

const attributes = [
  {
      id: "a",
      values: [
          {
              value: [
                  {
                      _id: "aa",
                      value: "1500"
                  },
                  {
                      _id: "ab",
                      value: "580"
                  }
              ]
          },
          {
              value: [
                  {
                      _id: "aa",
                      value: "400"
                  }
              ]
          }
      ]
  },
  {
      id: "a",
      values: [
          {
              value: [
                  {
                      _id: "aa",
                      value: "420"
                  },
                  {
                      _id: "ab",
                      value: "300"
                  }
              ]
          },
          {
              value: [
                  {
                      _id: "aa",
                      value: "480"
                  },
                  {
                      _id: "ab",
                      value: "1000"
                  }
              ]
          },
          {
              value: [
                  {
                      _id: "aa",
                      value: "880"
                  },
                  {
                      _id: "ab",
                      value: "740"
                  }
              ]
          }
      ]
  },
  {
      id: "b",
      values: [
          {
              value: [
                  {
                      _id: "ba",
                      value: "1500"
                  },
                  {
                      _id: "bb",
                      value: "580"
                  }
              ]
          },
          {
              value: [
                  {
                      _id: "ba",
                      value: "400"
                  }
              ]
          }
      ]
  },
];

newAttributes = [];
attributes.forEach(attribute => {
  childOutput = childNode(attribute.values)

  var isIdPresent = newAttributes.filter(e => {
    return  e.id == attribute.id
  });

  if (isIdPresent.length > 0) {
    var parentNode = isIdPresent[0]
    newAttributes = newAttributes.filter(e => {
      return  e.id != attribute.id
    });
    parentNode["values"][0].value = (mergeChildNode(parentNode["values"][0].value, childOutput))
    newAttributes.push(parentNode)
  } else {
    var parentNode ={}
    parentNode["id"] = attribute.id
    parentNode["values"] = [{value:[]}]
    parentNode["values"][0].value = (mergeChildNode(parentNode["values"][0].value, childOutput))
    newAttributes.push(parentNode)
  }
});

console.log(JSON.stringify(newAttributes));


function childNode(attrValues){
  childOutput = {}
  attrValues.forEach(valueArray => {
    valueArray.value.forEach(valueObj => {
      if (childOutput.hasOwnProperty(valueObj._id)) {
        childOutput[valueObj._id] = childOutput[valueObj._id] + parseInt(valueObj.value)
      } else {
        childOutput[valueObj._id] = parseInt(valueObj.value)
      }
    });
  });
  return childOutput
}

function mergeChildNode (inputArray, childOutput) {
  for (const property in childOutput) {
    var isIdPresent = inputArray.filter(e => {
      return  e._id == property
    });

    if (isIdPresent.length > 0) {
      var newObj = isIdPresent[0];
      inputArray = inputArray.filter(e => {
        return  e._id != property
      });
      newObj["values"].push(childOutput[property])
      inputArray.push(newObj)
    } else {
      inputArray.push({
        _id: property,
        values: [childOutput[property]]
      })
    }
  }
  return inputArray
} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.