Javascript - merge properties of an object - javascript

I am trying to find out some elegant solution how to merge values of object's properties if the name is same.
Example:
var object = {
"10-10-2017": "Black friday",
"11-09-2017": "Some holiday",
"10-10-2017": "Fathers day"
}
Merge to:
var object = {
"10-10-2017": "Black friday Fathers day",
"11-09-2017": "Some holiday",
}
I use this object as a feed for calendar where property name is date and property value is/are events for the date and my solution cannot handle two properties with the same name. This feed is generated by template engine and it cannot can only render it this way (for each context of an event it adds a line to the object) when a view is rendered.
For those who know Kentico CMS I am using repeater with effect to build this object, where " var object = {" is html envelope before and "} " is html envelope after.

For the first edit I suggest that you change your object into other form
from
var object = {
"10-10-2017": "Black friday",
"11-09-2017": "Some holiday",
"10-10-2017": "Fathers day"
}
to
var object =[
{"date":"10-10-2017","value":"Black friday"},
{"date":"11-09-2017","value":"Some holiday"},
{"date":"10-10-2017","value":"Fathers day"}
]
If that helps you here's the working solution
var object = [{date:"10-10-2017",value:"Black friday"},{date:"11-09-2017",value:"Some holiday"},{date:"10-10-2017",value:"Fathers day"}];
var output =[];
object.forEach(function(value) {
var existing = output.filter(function(v, i) {
return v.date == value.date;
});
if (existing.length) {
var existingIndex = output.indexOf(existing[0]);
output[existingIndex].value += ' '+value.value
} else {
if (typeof value.value == 'string')
value.value = value.value;
output.push(value);
}
});
console.log(JSON.stringify(output)); //returns [{"date":"10-10-2017","value":"Black friday Fathers day"},{"date":"11-09-2017","value":"Some holiday"}]

In case the question from my comment answers with Yes, this possibly provides a working approach ...
// BEGIN of CMS templating ...
var descriptorList = [];
// programmatically push into a list of property descriptors,
// each descriptor holding just one key value pair.
// +++ BEGIN iteration here ...
descriptorList.push({ "10-10-2017": "Black friday" });
// ...
// ... keep looping ... "Kentico CMS repeater"?
// for the sake of providing a working example
// there will be some more descriptors pushed ...
descriptorList.push({ "11-09-2017": "Some holiday" });
descriptorList.push({ "10-10-2017": "Fathers day" });
// ... END of iteration. +++
function mergeObjectsAndConcatSamePropertyStringTypes(a, b) {
var
type = Object.assign({}, a, b); // - start with a raw merged type (merger).
Object.keys(b).filter(function (key) { // - collect `key` duplicates.
return (key in a);
}).forEach(function (key) { // - for each duplicate `key` that targets
var aValue = a[key]; // a string type at each object, do concat
var bValue = b[key]; // this values and assign it to the merger.
if ((typeof aValue == 'string') &&/*||*/ (typeof bValue == 'string')) {
type[key] = [aValue, bValue].join(' ');
}
});
return type;
}
// assembling of the desired type ...
var type = descriptorList.reduce(mergeObjectsAndConcatSamePropertyStringTypes, {});
// END of CMS templating ...
console.log('descriptorList : ', descriptorList);
console.log('type : ', type);
.as-console-wrapper { max-height: 100%!important; top: 0; }

First of all your code is not correct as JS will only picks up the last prop if name is same.
> var object = {
... "10-10-2017": "Black friday",
... "11-09-2017": "Some holiday",
... "10-10-2017": "Fathers day"
... }
undefined
> object
{ '10-10-2017': 'Fathers day', '11-09-2017': 'Some holiday' }
>
But
For the extra prop, there is an solution, hope this helps you, e.g. your
object[newProperty] = (object[newProperty] || '') + newValue;

Related

Implementing an add and remove function into my class

I have this code below that takes items of a PC and returns a quote, price etc. As of now, the only way that I can add or remove components is by directly modifying the array. Here is the code
class PriceCalc {
Motherboard = 520.99;
RAM = 250.4;
SSD = 500.8;
HDD = 400.66;
Case = 375.5;
Monitor = 600.75;
Keyboard = 100.99;
Mouse = 25.5;
constructor(Obj) {
this.parts = Obj;
this.cost = "$" + Obj.reduce((a, b) => a + this[b], 0).toFixed(2);
this.retail = "$" + (Obj.reduce((a, b) => a + this[b], 0) + Obj.reduce((a, b) => a + this[b], 0) * 1.75).toFixed(2);
this.quote = "Your quote is " + this.retail;
}
}
This is the key values
quote = new PriceCalc(["Motherboard", "RAM", "SSD", "HDD", "Case", "Monitor", "Keyboard", "Mouse"]);
console.log(quote.parts);
console.log(quote.cost);
console.log(quote.retail);
console.log(quote.quote);
What Id like to implement is a code as shown below that I can add within the class and call it to add new parts to the quote or remove existing parts.
function removePart(arr, part) {
return arr.filter(function (ele) {
return ele != part;
});
} var result = removePart(["Motherboard", "RAM", "SSD", "HDD", "Case", "Monitor", "Keyboard", "Mouse"], "Mouse");
console.log(result)
I would like to implement it in a way where I can call this.parts (as stated in the previous code) and either call a new function that pushes new parts to "parts" or call a new function that removes certain parts that are already in the list. I hope what I've described makes sense
Here is s a general example template of what I mean, without the functional code:
class quote {
constructor(/*...*/) {
this.parts = ....
}
add() {
//adds item
}
remove() {
//removes item
}
}
example = new quote(["part1", "part2"])
add(part3)
console.log(example) //returns ["part1" "part2" "part3"]
remove(part1)
console.log(example) //returns ["part2" "part3"]
This should work
class quote {
constructor(initialParts) {
this.parts = initialParts
}
add(part) {
this.parts = [...this.parts, part]
}
remove(part) {
this.parts = this.parts.filter( p => p !== part)
}
}
example = new quote(["part1", "part2"])
example.add('part3')
console.log(example) //returns ["part1" "part2" "part3"]
example.remove('part1')
console.log(example) //returns ["part2" "part3"]
If you want multiple of the same item you can still use an object and store the key as the name of the product and the number of items as the value. simply adjust the functions to account for this.

Combine JSON Objects with same number of key-value pair, appendinging the main key values

I have following JSON objects in one of my parser programs written years ago.
{"Date":"19/02/16","Narration":"NEFT DR-BARB0PIMPAL-Govt. NE","Chq":{"/Ref":{"No":{"":"N050160130709970"}}},"Value Dt":"19/02/16","Withdrawal Amt":{"":"8,000.00"},"Deposit Amt":{"":""},"Closing Balance":"24,114.95"}
{"Date":"","Narration":"TBANK, MUM-N050160130709970","Chq":{"/Ref":{"No":{"":""}}},"Value Dt":"","Withdrawal Amt":{"":""},"Deposit Amt":{"":""},"Closing Balance":""}
Basically, the above entry should have been a single record. However, due to line wrapping the parser treating them as a new record.
I tried mapping the objects by converting them into an array. However, then the map resulted as the following JSON object.
{"Date":"19/02/16"}{"Date":""},{"Narration":"NEFT DR-BARB0PIMPAL-Govt. NE"}{"Narration":"TBANK, MUM-N050160130709970"},{"Chq":{"/Ref":{"No":{"":"N050160130709970"}}}}{"Chq":{"/Ref":{"No":{"":""}}}},{"Value Dt":"19/02/16"}{"Value Dt":""},{"Withdrawal Amt":{"":"8,000.00"}}{"Withdrawal Amt":{"":""}},{"Deposit Amt":{"":""}}{"Deposit Amt":{"":""}},{"Closing Balance":"24,114.95"}{"Closing Balance":""}
What I need to have is a merged/combined array OR object with main keys remain intact and values join/append to the first element (second object to be discarded thereafter.)
For example.
{"Date":"19/02/16","Narration":"NEFT DR-BARB0PIMPAL-Govt.NETBANK","Chq":{"/Ref":{"No":{"":"N050160130709970MUM-N050160130709970"}}},"Value Dt":"19/02/16","Withdrawal Amt":{"":"8,000.00"},"Deposit Amt":{"":""},"Closing Balance":"24,114.95"}
Can you guys suggest if I can directly merge/combine the source JSON Object or merge the arrays with above effects?
Obviously you would need to know the order of these objects, so I will assume you have an array of them, all parsed from JSON into JavaScript objects.
Secondly, I assume that a record that is a continuation of the previous one can be recognised by an empty balance field (for instance -- adapt as needed).
Here is code to perform the merge:
function mergeSplitObjects(arr) {
return arr.filter( (curr, i) => {
// Does this look like a split-off object that should be merged with previous?
if (curr["Closing Balance"] !== "") { // Adapt condition as needed
return true; // No, it is a real record
}
arr[i-1]["Narration"] += curr["Narration"]; // Merge, and filter curr out
});
}
// Sample data with 3 records. First two should be merged
let arr = [
{"Date":"19/02/16","Narration":"NEFT DR-BARB0PIMPAL-Govt. NE","Chq":{"/Ref":{"No":{"":"N050160130709970"}}},"Value Dt":"19/02/16","Withdrawal Amt":{"":"8,000.00"},"Deposit Amt":{"":""},"Closing Balance":"24,114.95"},
{"Date":"","Narration":"TBANK, MUM-N050160130709970","Chq":{"/Ref":{"No":{"":""}}},"Value Dt":"","Withdrawal Amt":{"":""},"Deposit Amt":{"":""},"Closing Balance":""},
{"Date":"20/02/16","Narration":"ATM NYC 13","Chq":{"/Ref":{"No":{"":"N050160130709971"}}},"Value Dt":"20/02/16","Withdrawal Amt":{"":"1,000.00"},"Deposit Amt":{"":""},"Closing Balance":"23,114.95"},
];
arr = mergeSplitObjects(arr);
console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I would use a function similar to Object.assign.
Just like Trincot, I assume Closing Balance to be an indication for records to merge.
var splitRegExp = /[a-z],\s*[a-z]/gi;
function deepMergingAssign(obj /*, sourceObjects*/){
'use strict';
if (obj == null) throw new TypeError('Cannot convert undefined or null to object');
var final = Object(obj);
[].slice.call(arguments, 1).forEach(function(arg){
if(arg != null) {
for(var prop in arg) {
if (typeof arg[prop] === 'object'&&typeof final[prop] === 'object') {
// recursively call this function
arg[prop] = deepMergingAssign(final[prop],arg[prop]);
delete arg[prop]
}
else if(Object.prototype.hasOwnProperty.call(arg,prop)) {
// append the new values to the existing value.
var currentValue = final[prop].split(splitRegExp);
var incomingValue = arg[prop].split(splitRegExp);
incomingValue.forEach( function(val){
if (val! == '' && currentValue.indexOf(val) === -1) currentValue.push(val);
});
final[prop] = currentValue.join(', ');
}
}
}
});
return final;
}
function mergeRecords(records){
var toMerge = [];
var mergedData = []
for (var i=0; i<records.length; i++){
// Change this condition as needed
if (records[i]['Closing Balance'] !== '' && toMerge.length>0){
// Create a merged record and reset the toMerge variable
mergedData.push(deepMergingAssign.apply(null, toMerge));
toMerge = [records[i]];
}
else {
// This record should be merged with the previous.
toMerge.push(records[i]);
}
}
// Merge the last records stored in the array
mergedData.push(deepMergingAssign.apply(null, toMerge));
return mergedData;
}
var allRecords = [
{"Date":"19/02/16","Narration":"NEFT DR-BARB0PIMPAL-Govt. NE","Chq":{"/Ref":{"No":{"":"N050160130709970"}}},"Value Dt":"19/02/16","Withdrawal Amt":{"":"8,000.00"},"Deposit Amt":{"":""},"Closing Balance":"24,114.95"},
{"Date":"","Narration":"TBANK, MUM-N050160130709970","Chq":{"/Ref":{"No":{"":""}}},"Value Dt":"","Withdrawal Amt":{"":""},"Deposit Amt":{"":""},"Closing Balance":""},
{"Date":"21/02/16","Narration":"TBANK","Chq":{"/Ref":{"No":{"":"N050160130709971"}}},"Value Dt":"21/02/16","Withdrawal Amt":{"":""},"Deposit Amt":{"":"2,000.00"},"Closing Balance":"26,114.95"},
{"Date":"22/02/16","Narration":"TBANK","Chq":{"/Ref":{"No":{"":"N050160130709972"}}},"Value Dt":"22/02/16","Withdrawal Amt":{"":"5,750.00"},"Deposit Amt":{"":"1,000.00"},"Closing Balance":"21,364.95"},
{"Date":"","Narration":"TBANK, MUM-N050160130709972","Chq":{"/Ref":{"No":{"":""}}},"Value Dt":"","Withdrawal Amt":{"":""},"Deposit Amt":{"":""},"Closing Balance":""}
]
var merged = mergeRecords(allRecords);
/* [
record 1+2,
record 3,
record 4+5
] */

Pouchdb join / link documents

I have pouchdb/couchbase data with equipment that has user assigned to them.
Equipment with _id and in the equipment doc there is a checkedOutBy with the user._id as the value. Within the employee object there is user.name. When I get the equipment objects how do I also get the user.name and display with the equipment.
I have searched and read about map/reduce that uses emit and do not grasp the idea. My code that i wrote from what i learned is:
by the way I am also using Angularjs.
field = "eq::"
this.getAllEquip = function(field){
function map(doc) {
if (doc.checkedOutBy !== undefined) {
emit(doc.checkedOutBy, {empName : doc.name});
}
}
var result = database.query(map, {include_docs: true,
attachments: true,
startkey: field,
endkey: field + '\uffff'})
.catch(function (err) {
//error stuff here
});
return result
};
I don't see where the two docs would get together. What am i missing? My result is empty.
The equipment json looks like:
{checkedOutBy: "us::10015", description: "3P Microsoft Surface w/stylus & power cord", equipId: "SUR1501", purchaseDate: "", rCost: 1000, id:"eq::10001"}
Emlpoyee json:
{"firstname":"Joe","gender":"male","lastname":"Blow","status":"active","title":"office","type":"userInfo","_id":"us::10015","_rev":"2-95e9f34784094104ad24bbf2894ae786"}
Thank you for your help.
Something like this should work, if I understood the question correctly:
//Sample Array of Objects with Equipment
var arr1=[{checkedout:"abc1",desc:"item1",id:1},
{checkedout:"abc2",desc:"item2",id:2},
{checkedout:"abc3",desc:"item3",id:3},
{checkedout:"abc1",desc:"item1",id:4},
{checkedout:"abc4",desc:"item3",id:5},
{checkedout:"abc6",desc:"item3",id:6}];
//Sample array of objects with Employee - the "id" in arr2 matches with "checkout" in arr1
var arr2=[{name:"john",id:"abc1"},
{name:"jack",id:"abc2"},
{name:"alice",id:"abc3"},
{name:"james",id:"abc4"}];
var result = []; //final result array
//loop through equipment array arr1
arr1.forEach(function(obj) {
var tempObj = obj;
var checkedout_id=obj.checkedout;
//do array.find which will return the first element in the array which satisfies the given function. This is absed on the assumption that that the id is unique for employee and there wont bwe multiple employees with same id (which is the "checkedout" field in equipment. If the employee is not found, it will return undefined.
var foundname = arr2.find(function(obj) {
if (obj.id == checkedout_id)
return obj.name
})
//Create the object to be inserted into the final array by adding a new key called "name", based on the result of above find function
if (foundname != undefined) {
tempObj.name=foundname.name
}
else {
tempObj.name = "Not found";
}
result.push(tempObj);
})
This is my Pouchdb solution, thank you Vijay for leading me to this solution.
First I get all my equipment. Then I use Vijay's idea to loop through the array and add the name to the object and build new array. I found there is a need to go into the .doc. part of the object as in obj.doc.checkedOutBy and tempObj.doc.name to get the job done.
$pouchDB.getAllDocs('eq::').then(function(udata){
var result = [];
//loop through equipment array
udata.rows.forEach(function(obj) {
var tempObj = obj;
var checkedout_id=obj.doc.checkedOutBy;
if (checkedout_id != undefined) {
$pouchDB.get(checkedout_id).then(function(emp){
return emp.firstname + " " + emp.lastname
}).then(function(name){
tempObj.doc.name = name;
});
}
result.push(tempObj);
})
in my service I have:
this.get = function(documentId) {
return database.get(documentId);
};
and:
this.getAllDocs = function(field){
return database.allDocs({
include_docs: true,
attachments: true,
startkey: field,
endkey: field + '\uffff'});
};

JAVASCRIPT object to array conversion

I search on stackoverflow before post my question, but I didn't find any solution. I have an object like this :
"{"COURRIERS":
{"05. Juridique":
[{"res_id":100,"type_label":"Plainte","subject":"test23","doctypes_first_level_label":"COURRIERS","doctypes_second_level_label":"05. Juridique","folder_level":2}]
}
}"
And I need to access it like an array, in order to get the information like res_id etc..
How can I do this ?
Thanks in advance
Assuming that you won't have more than one object/array in each layer, this should get you what you need.
let obj = {
"COURRIERS": {
"05. Juridique": [{
"res_id": 100,
"type_label": "Plainte",
"subject": "test23",
"doctypes_first_level_label": "COURRIERS",
"doctypes_second_level_label": "05. Juridique",
"folder_level": 2
}]
}
}
let folder = Object.keys(obj)[0]
let type = Object.keys(obj[folder])[0]
let result = obj[folder][type][0]
console.log(result)
You can gain access to the data in multiple ways. The following below will help clarify some of the way you can access some of the data.
myObj.type = "Dot syntax";
myObj.type = "Dot syntax";
myObj["date created"] = "String with space";
myObj[str] = "String value";
myObj[rand] = "Random Number";
myObj[obj] = "Object";
myObj[""] = "Even an empty string";
For your problem you can use the following
var x = {
"COURRIERS":{
"05. Juridique":[
{
"res_id":100,
"type_label":"Plainte",
"subject":"test23",
"doctypes_first_level_label":"COURRIERS",
"doctypes_second_level_label":"05. Juridique",
"folder_level":2
}
]
}};
console.log(x['COURRIERS']['05. Juridique'][0].res_id)
Something like that ?
(I insert the data inside a variable and print the wanted result with key index)
let obj = {
"COURRIERS":{
"05. Juridique":[
{
"res_id":100,
"type_label":"Plainte",
"subject":"test23",
"doctypes_first_level_label":"COURRIERS",
"doctypes_second_level_label":"05. Juridique",
"folder_level":2
}
]
}
};
console.log(obj["COURRIERS"]["05. Juridique"][0]["res_id"]);
EDIT
You want to acess it with variable.
For avoid bug, I strong recommend you to check if the variable value key exist in the array/object like :
let folder = 'COURRIERS';
if(folder.indexOf(data) >= 0) { // folder.indexOf(data) = 0
// ... finish the job here :)
}
// indexOf return -1 if the value is not found

javascript String to associative object

I am just wondering if there is an easy way to create an associative object from a string that needs double split, thisstring is the result from an api call, so length of the object could change.
For instance, if I have a string that looks like this:
var infoValue = 'Loan Date~Loan Number~Loan Amount|15/03/2016~1042~620|15/03/2016~1044~372';
I want to have an object that looks like this:
[
{
"Loan Date":"15/03/2016",
"Loan Number":"1042",
"Loan Amount":"620",
},
{
"Loan Date":"15/03/2016",
"Loan Number":"1042",
"Loan Amount":"620",
}
]
What I am doing right now is something like
var res = infoValue.split("|");
var activeLoans = new Array();
for(field in res) {
if(res[field] != ''){
activeLoans.push(res[field]);
}
}
for(field in activeLoans){
var row = activeLoans[field];
rowSplit = row.split("~");
}
But I am not happy with this approach, as I need to create a table to display this data, and the site that I am getting this api might change the order of the response of the string, or might add other values
What you have done is about all you can do, though I would not use for..in for a typical array. You should be able to deal with any sequence of values, as long as the header is consistent with the rest of the data, e.g.
var infoValue = 'Loan Date~Loan Number~Loan Amount|15/03/2016~1042~620|15/03/2016~1044~372';
function parseInfoValue(s) {
var b = s.split('|');
var header = b.shift().split('~');
return b.reduce(function(acc, data) {
var c = data.split('~');
var obj = {};
c.forEach(function(value, i){
obj[header[i]] = value;
})
acc.push(obj);
return acc;
},[]);
}
var x = parseInfoValue(infoValue);
document.write(JSON.stringify(x));
This will create the required structure no matter how many items are in each record, it just needs a label for each item in the header part and a value for each item (perhaps empty) in every data part.
Edit
Thinking on it a bit more, I don't know why I used forEach internally when reduce is the obvious candidate:
var infoValue = 'Loan Date~Loan Number~Loan Amount|15/03/2016~1042~620|15/03/2016~1044~372';
function parseInfoValue(s) {
var b = s.split('|');
var header = b.shift().split('~');
return b.reduce(function(acc, data) {
acc.push(data.split('~').reduce(function(obj, value, i) {
obj[header[i]] = value;
return obj;
}, {}));
return acc;
}, []);
}
var x = parseInfoValue(infoValue);
document.write(JSON.stringify(x));

Categories

Resources