Related
I have the following json object:
[{
"WEIGHTED_ARR_LAST_SLP": "0.03801186624130076",
"SLIPPAGE_INTERVAL_VWAP_BPS": "10.2711",
"ROOT_ORDER_ID": "735422197553491",
"ARRIVAL_MID_PX": "93.6100",
"WEIGHTED_ARR_SLP": "0.12323190317127024",
"AVG_PX": "93.6586",
"LEAVES_QTY": "0",
"WEIGHT": "0.02372627566400397",
"PARTICIPATION_RATE": "0E-12",
"LOCAL_REF_END_TIME": "2016-09-06 06:00:27.775",
"WEIGHTED_IVWAP_SLP": "0.2436949499725512",
"NOTIONAL_USD": "477940",
"LIQ_CONSUMPTION": "15.21",
"EXEC_QTY": "5103",
"CL_ORD_ID": "7245294057012908344",
"LOCAL_REF_START_TIME": "2016-09-06 05:59:57.844",
"SLIPPAGE_END_LAST_ARR_LAST_BPS": "1.6021",
"IVWAP_PX": "93.5625",
"LIMIT_PX": "93.6100",
"ORDER_ID": "735422197553491",
"SLIPPAGE_ARR_MID_BPS": "5.1939",
"ORDER_QTY": "5103",
"EXECUTION_STYLE": "2", {"ORDER_QTY": "5100", ..........}
}]
I want to coonvert only "Ord Qty", "Exec Qty", "AVG Px", "Notional", "Limit Px", "Arrival Px" "Arrival Px Slpg", "IVWAP Px Slpg", "LIQ Consumption" to Number; and then display WEIGHTED_IVWAP_SLP & WEIGHTED_ARR_SLP with 4 decimal points, instead of so many. I'm trying to do it in the following way:
var jsondata = document.getElementById("jsonArray").value;
var jsondataObj = JSON.parse(jsondata);
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
var parsedData = jsondataObj.map(function(obj) {
return Object.keys(obj).reduce(function(memo, key) {
var value = obj[key];
//console.log(value);
memo[key] = isNumeric(value) ? Number(value).toFixed(4) : value;
//localStorage.setItem("storeditem", value);
return memo;
}, {})
});
But this is converting all of fields of numbers with 4 decimal point. How to convert only specific fields?
This is kind of doing your job for you right? Well, this answer doesn't use your code, but you should be able to apply it just the same. This uses language constructs such as if, for and for ... in as opposed to methods of Array like map, reduce, and filter.
var a = [{
"WEIGHTED_ARR_LAST_SLP": "0.03801186624130076",
"SLIPPAGE_INTERVAL_VWAP_BPS": "10.2711",
"ROOT_ORDER_ID": "735422197553491",
"ARRIVAL_MID_PX": "93.6100",
"WEIGHTED_ARR_SLP": "0.12323190317127024",
"AVG_PX": "93.6586",
"LEAVES_QTY": "0",
"WEIGHT": "0.02372627566400397",
"PARTICIPATION_RATE": "0E-12",
"LOCAL_REF_END_TIME": "2016-09-06 06:00:27.775",
"WEIGHTED_IVWAP_SLP": "0.2436949499725512",
"NOTIONAL_USD": "477940",
"LIQ_CONSUMPTION": "15.21",
"EXEC_QTY": "5103",
"CL_ORD_ID": "7245294057012908344",
"LOCAL_REF_START_TIME": "2016-09-06 05:59:57.844",
"SLIPPAGE_END_LAST_ARR_LAST_BPS": "1.6021",
"IVWAP_PX": "93.5625",
"LIMIT_PX": "93.6100",
"ORDER_ID": "735422197553491",
"SLIPPAGE_ARR_MID_BPS": "5.1939",
"ORDER_QTY": "5103",
"EXECUTION_STYLE": "2"
}];
var fieldsToConvertToNumbers = [
"ORDER_QTY", "EXEC_QTY", "AVG_PX",
"NOTIONAL_USD", "LIMIT_PX", "ARRIVAL_MID_PX",
"WEIGHTED_IVWAP_SLP", "LIQ_CONSUMPTION"
];
for (var i = 0; i < a.length; ++i) {
var b = a[i];
for (var j = 0; j < fieldsToConvertToNumbers.length; ++j) {
var field = fieldsToConvertToNumbers[j];
if (field in b) {
var value = parseFloat(b[field]);
if (!isNaN(value))
b[field] = value;
}
}
}
var pre = document.createElement("pre");
pre.appendChild(document.createTextNode(JSON.stringify(a, null, ' ')));
document.body.appendChild(pre);
Use parseFloat to convert the values to numbers, but be sure to check for NaN results before deciding to update the value. Use IsNaN for that. Don't think you really need to use !IsFinite, but knock yourself out.
So now you want to use toFixed to fix your string display.
var text = (3.1415926535897932384626433832795028841971).toFixed(4);
var pre = document.createElement("pre");
pre.appendChild(document.createTextNode(text));
document.body.appendChild(pre);
So now you can use toFixed to convert your numbers to strings that have only 4 decimal places of precision. However, they are strings instead of numbers. So you have to parse them back using parseFloat.
var a = [{
"WEIGHTED_ARR_LAST_SLP": "0.03801186624130076",
"SLIPPAGE_INTERVAL_VWAP_BPS": "10.2711",
"ROOT_ORDER_ID": "735422197553491",
"ARRIVAL_MID_PX": "93.6100",
"WEIGHTED_ARR_SLP": "0.12323190317127024",
"AVG_PX": "93.6586",
"LEAVES_QTY": "0",
"WEIGHT": "0.02372627566400397",
"PARTICIPATION_RATE": "0E-12",
"LOCAL_REF_END_TIME": "2016-09-06 06:00:27.775",
"WEIGHTED_IVWAP_SLP": "0.2436949499725512",
"NOTIONAL_USD": "477940",
"LIQ_CONSUMPTION": "15.21",
"EXEC_QTY": "5103",
"CL_ORD_ID": "7245294057012908344",
"LOCAL_REF_START_TIME": "2016-09-06 05:59:57.844",
"SLIPPAGE_END_LAST_ARR_LAST_BPS": "1.6021",
"IVWAP_PX": "93.5625",
"LIMIT_PX": "93.6100",
"ORDER_ID": "735422197553491",
"SLIPPAGE_ARR_MID_BPS": "5.1939",
"ORDER_QTY": "5103",
"EXECUTION_STYLE": "2"
}];
var fieldsToConvertToNumbers = [
"ORDER_QTY", "EXEC_QTY", "AVG_PX",
"NOTIONAL_USD", "LIMIT_PX", "ARRIVAL_MID_PX",
"LIQ_CONSUMPTION"
];
var fieldsToConvertToFixedNumbers = [
"WEIGHTED_IVWAP_SLP", "WEIGHTED_ARR_SLP"
];
for (var i = 0; i < a.length; ++i) {
var b = a[i];
for (var j = 0; j < fieldsToConvertToNumbers.length; ++j) {
var field = fieldsToConvertToNumbers[j];
if (field in b) {
var value = parseFloat(b[field]);
if (!isNaN(value))
b[field] = value;
}
}
for (var j = 0; j < fieldsToConvertToFixedNumbers.length; ++j) {
var field = fieldsToConvertToFixedNumbers[j];
if (field in b) {
var value = parseFloat(b[field]);
if (!isNaN(value))
b[field] = parseFloat(value.toFixed(4));
}
}
}
var pre = document.createElement("pre");
pre.appendChild(document.createTextNode(JSON.stringify(a, null, ' ')));
document.body.appendChild(pre);
So now only the specific fields have 4 decimal places. And they're numbers, not strings.
You can modify your code slightly by adding a filter on the keys, before launching the reduce(). Secondly, you could put an extra condition in your ternary operator, to deal with the two fields that should get 4 decimal digits:
var parsedData = jsondataObj.map(function(obj) {
return Object.keys(obj).filter (
key => ["ORDER_QTY", "EXEC_QTY", "AVG_PX", "NOTIONAL_USD", "LIMIT_PX",
"ARRIVAL_MID_PX", "WEIGHTED_ARR_SLP", "WEIGHTED_IVWAP_SLP",
"LIQ_CONSUMPTION"].includes(key)
).reduce(function(memo, key) {
var value = obj[key];
memo[key] = !isNumeric(value) ? value
: ["WEIGHTED_IVWAP_SLP", "WEIGHTED_ARR_SLP"].includes(key)
? Number(value).toFixed(4)
: Number(value);
return memo;
}, Object.assign({}, obj))
});
Note that you must then also pass a copy of the original object as initial value for reduce. For this Object.assign can be used.
Also, be aware that .toFixed turns a number back to string type, as this is the only way to actually ensure you see 4 decimal digits. If you want to really have the number type, then apply Number() to that result again:
Number(Number(value).toFixed(4))
Of course, trailing decimal zeroes will not display when you output such numbers without formatting.
var jsondata = `[{
"WEIGHTED_ARR_LAST_SLP": "0.03801186624130076",
"SLIPPAGE_INTERVAL_VWAP_BPS": "10.2711",
"ROOT_ORDER_ID": "735422197553491",
"ARRIVAL_MID_PX": "93.6100",
"WEIGHTED_ARR_SLP": "0.12323190317127024",
"AVG_PX": "93.6586",
"LEAVES_QTY": "0",
"WEIGHT": "0.02372627566400397",
"PARTICIPATION_RATE": "0E-12",
"LOCAL_REF_END_TIME": "2016-09-06 06:00:27.775",
"WEIGHTED_IVWAP_SLP": "0.2436949499725512",
"NOTIONAL_USD": "477940",
"LIQ_CONSUMPTION": "15.21",
"EXEC_QTY": "5103",
"CL_ORD_ID": "7245294057012908344",
"LOCAL_REF_START_TIME": "2016-09-06 05:59:57.844",
"SLIPPAGE_END_LAST_ARR_LAST_BPS": "1.6021",
"IVWAP_PX": "93.5625",
"LIMIT_PX": "93.6100",
"ORDER_ID": "735422197553491",
"SLIPPAGE_ARR_MID_BPS": "5.1939",
"ORDER_QTY": "5103",
"EXECUTION_STYLE": "2"
}]`;
var jsondataObj = JSON.parse(jsondata);
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
var parsedData = jsondataObj.map(function(obj) {
return Object.keys(obj).filter (
key => ["ORDER_QTY", "EXEC_QTY", "AVG_PX", "NOTIONAL_USD", "LIMIT_PX",
"ARRIVAL_MID_PX", "WEIGHTED_ARR_SLP", "WEIGHTED_IVWAP_SLP",
"LIQ_CONSUMPTION"].includes(key)
).reduce(function(memo, key) {
var value = obj[key];
memo[key] = !isNumeric(value) ? value
: ["WEIGHTED_IVWAP_SLP", "WEIGHTED_ARR_SLP"].includes(key)
? Number(value).toFixed(4)
: Number(value);
return memo;
}, Object.assign({}, obj))
});
console.log(parsedData);
Here you have a practical example:
var x = {
a: '1.11111',
b: '2.22222',
c: '3.33333',
d: '4.444444',
e: '5.55555'
}
var y = ['a', 'd'];
for(key in x) {
if(y.indexOf(key) != -1) {
x[key] = Number(x[key]).toFixed(2);
}
}
console.log(x);
X is your object and Y is an array containing the list of keys you want to convert to fixed, or whatever you want to do.
When going through the keys, if the key is inside that array, you do your thing.
https://jsfiddle.net/dtuLxzy6/
I have an array as following.
var vehicle = new Array();
vehicle.push( { "name": "honda", "value": "civic" } );
vehicle.push( { "name": "toyota", "value": "camry" } );
How can I modify this array to change civic to accord?
If you don't know the position of the object in the array, then you can simply loop through the array. When you find the value, replace it and exit the loop. If there could be more than one value of "civic" in the array, then leave out the break; statement and the loop will change all instances of "civic" to "accord".
for(var a = 0, len = vehicle.length; a < len; a++) {
if(vehicle[a].value === "civic") {
vehicle[a].value = "accord";
break;
}
}
Try this
vehicle[0].value = 'accord';
And for the array entirely
vehicle.map(function (vehicle) {
if (vehicle.value === 'civic') {
vehicle.value = 'accord';
}
});
Just do this:
vehicle[0].value = 'accord';
Try vehicle[0].value = "accord"
see JS Fiddle
vehicle[0]['value']='accord';
for (var i = 0, len = vehicle.length; i < len; i++) {
if (vehicle[i].value === "civic") {
vehicle[i].value = "accord";
}
}
I am making some visualisations and need to merge certain json entries.
The problem is, that the json file that gets called is a bit messed up. Certain entries need to be merged and others should be kept the same.
The json i get is something like this:
[
{
"label": "de",
"visits": 80,
},
{
"label": "fr",
"visits": 80,
},
{
"label": "/de",
"visits": 80,
},
{
"label": "/fr",
"visits": 80,
},
{
"label": "Aktuell",
"visits": 80,
},
{
"label": "fr/Aktuell",
"visits": 80,
},
{
"label": "index",
"visits": 80,
}
]
What i need is:
[
{
"label": "de",
"visits": 160,
},
{
"label": "fr",
"visits": 160,
},
{
"label": "Aktuell",
"visits": 160,
},
{
"label": "index",
"visits": 80,
}
]
The entries with labels "de" and "/de" as well as "fr" and "/fr" or "fr/Aktuell" and "Aktuell" need to be merged while other entries schould be kept the same like "index".
Is there a way to do this using d3?
I guess only using javascript I would have to get the json file, then search for the entries, create a new entry and add up the numbers to then take the original json, delete those entries and add the new entries... although i also wouldn't know exactly how to do that either.
And it also isn't the best solution, since i have to generate a second object that will have to be processed and slows the whole system down. We are having performance issues already and i don't want to create new bottle necks.
Another problem is, that i need to be able to extend the list of entries that should be merged...
Is there a fast and simple way of doing this?
I know this seems like a "hey, i am too lazy, do it for me" post.
I can assure you it isn't. It's more like a " i googled, i read, i tried, i sweared and i cried. now i am asking the professionals for help" post.
UPDATE:
I don't want to filter only on the last part of '/'. It was just an example. The filter would me more like: combine all [en, EN, /en, english, anglais] to one with label en. Also combine all [de, DE, /de, deutsch, german] to one with label de. Keep all others that are not combined.
Javascript should be enough for this:
function clean(arr) {
var obj = {};
// obj will contain a map with keys being 'label' and value being 'visits'
for (var i = 0; i < arr.length; i++) {
var labelParts = arr[i].label.split('/')
var label = labelParts[labelParts.length-1]
// Do a bit of filtering on the label
if(typeof obj[label]!=='undefined') {
obj[label] += arr[i].visits
} else {
obj[label] = arr[i].visits
}
}
return Object.keys(obj).map(function (key) { return {label:key,visits:obj[key]}; });
// Re-create an array out of the object
}
I assumed you wanted to filter on the last part after '/'.
jsFiddle: http://jsfiddle.net/4peN9/2/
I'd look into D3's nest operations. It shouldn't be too hard to specify the appropriate key and rollup functions to get what you want. Something to the effect of:
var cleanup = function(l) {
var i = l.indexOf("/");
return i < 0 ? l : l.substring(i+1, l.length);
};
var getVisits = function(d) { return d.visits; };
var sum = function(a, b) { return a + b; };
var nest = d3.nest()
.key(function(d) { return cleanup(d.label); })
.rollup(function(a) { return a.map(getVisits).reduce(sum); });
jsFiddle: http://jsfiddle.net/4peN9/3/
I now wrote a quite ugly but functional javascript script for this. I am sure that it could be done more efficiently but this shall do for me atm.
And it goes something like this:
function clean(arr)
{
mrgLst = [
{
"valid": "de",
"invalid": ["/de"]
},
{
"valid": "fr",
"invalid": ["/fr"]
},
{
"valid": "en",
"invalid": ["/en"]
},
{
"valid": "it",
"invalid": ["/it"]
},
{
"valid": "/index",
"invalid": ["/index.html"]
}
]
var obj = [];
for (var i = 0; i < mrgLst.length; i++)
{
var valid = mrgLst[i].valid;
var invalid = mrgLst[i].invalid;
for (var j = 0; j < arr.length; j++)
{
var test0 = arr[j].label
if (test0 == valid)
{
var subObj = {};
subObj["label"] = valid
subObj["visits"] = arr[j].visits
for (var k = 0; k < arr.length; k++)
{
var test1 = arr[k].label
for (x in invalid)
{
if (test1 == invalid[x])
{
subObj["label"] = valid
subObj["visits"] += arr[k].visits
}
}
}
}
}
if (subObj != undefined)
{
obj.push(subObj)
}
}
for (var i = 0; i < arr.length; i++)
{
var original = arr[i]
var org = {}
var dont = false
for (var j = 0; j < mrgLst.length; j++)
{
var test0 = mrgLst[j].valid
var test1 = mrgLst[j].invalid
if (original.label == test0)
{
dont = true
}
for (x in test1)
{
if (original.label == test1[x])
{
dont = true
}
}
}
if (dont == false)
{
org["label"] = original.label
org["visits"] = arr[i].visits
obj.push(org)
}
}
return obj
}
Just as reference for anybody that has a similar problem. Doesn't have much to do with d3 anymore but the output is used with d3...
I am trying to figure out an efficient way to remove objects that are duplicates from an array and looking for the most efficient answer. I looked around the internet everything seems to be using primitive data... or not scalable for large arrays. This is my current implementation which is can be improved and want to try to avoid labels.
Test.prototype.unique = function (arr, artist, title, cb) {
console.log(arr.length);
var n, y, x, i, r;
r = [];
o: for (i = 0, n = arr.length; i < n; i++) {
for (x = 0, y = r.length; x < y; x++) {
if (r[x].artist == arr[i].artist && r[x].title == arr[i].title) {
continue o;
}
}
r.push(arr[i]);
}
cb(r);
};
and the array looks something like this:
[{title: sky, artist: jon}, {title: rain, artist: Paul}, ....]
Order does not matter, but if sorting makes it more efficient then I am up for the challenge...
and for people who do not know o is a label and it is just saying jump back to the loop instead of pushing to the new array.
Pure javascript please no libs.
ANSWERS SO FAR:
The Performance Test for the answers below:
http://jsperf.com/remove-duplicates-for-loops
I see, the problem there is that the complexity is squared. There is one trick to do it, it's simply by using "Associative arrays".
You can get the array, loop over it, and add the value of the array as a key to the associative array. Since it doesn't allow duplicated keys, you will automatically get rid of the duplicates.
Since you are looking for title and artist when comparing, you can actually try to use something like:
var arrResult = {};
for (i = 0, n = arr.length; i < n; i++) {
var item = arr[i];
arrResult[ item.title + " - " + item.artist ] = item;
}
Then you just loop the arrResult again, and recreate the array.
var i = 0;
var nonDuplicatedArray = [];
for(var item in arrResult) {
nonDuplicatedArray[i++] = arrResult[item];
}
Updated to include Paul's comment. Thanks!
Here is a solution that works for me.
Helper functions:
// sorts an array of objects according to one field
// call like this: sortObjArray(myArray, "name" );
// it will modify the input array
sortObjArray = function(arr, field) {
arr.sort(
function compare(a,b) {
if (a[field] < b[field])
return -1;
if (a[field] > b[field])
return 1;
return 0;
}
);
}
// call like this: uniqueDishes = removeDuplicatesFromObjArray(dishes, "dishName");
// it will NOT modify the input array
// input array MUST be sorted by the same field (asc or desc doesn't matter)
removeDuplicatesFromObjArray = function(arr, field) {
var u = [];
arr.reduce(function (a, b) {
if (a[field] !== b[field]) u.push(b);
return b;
}, []);
return u;
}
and then simply call:
sortObjArray(dishes, "name");
dishes = removeDuplicatesFromObjArray(dishes, "name");
Basic sort-then-unique implementation, fiddle HERE:
function unique(arr) {
var comparer = function compareObject(a, b) {
if (a.title == b.title) {
if (a.artist < b.artist) {
return -1;
} else if (a.artist > b.artist) {
return 1;
} else {
return 0;
}
} else {
if (a.title < b.title) {
return -1;
} else {
return 1;
}
}
}
arr.sort(comparer);
console.log("Sorted: " + JSON.stringify(arr));
for (var i = 0; i < arr.length - 1; ++i) {
if (comparer(arr[i], arr[i+1]) === 0) {
arr.splice(i, 1);
console.log("Splicing: " + JSON.stringify(arr));
}
}
return arr;
}
It may or may not be the most efficient, and should be entirely scalable. I've added some console.logs so you can see it as it works.
EDIT
In the interest of saving on the space the function used, I did that for loop at the end, but it seems likely that didn't properly find only unique results (depsite it passing my simple jsfiddle test). Please try replacing my for loop with the following:
var checker;
var uniqueResults = [];
for (var i = 0; i < arr.length; ++i) {
if (!checker || comparer(checker, arr[i]) != 0) {
checker = arr[i];
uniqueResults.push(checker);
}
}
return uniqueResults;
I use this function. its not doing any sorting, but produces result. Cant say about performance as never measure it.
var unique = function(a){
var seen = [], result = [];
for(var len = a.length, i = len-1; i >= 0; i--){
if(!seen[a[i]]){
seen[a[i]] = true;
result.push(a[i]);
}
}
return result;
}
var ar = [1,2,3,1,1,1,1,1,"", "","","", "a", "b"];
console.log(unique(ar));// this will produce [1,2,3,"", "a", "b"] all unique elements.
Below is Henrique Feijo's answer with ample explanation and an example that you can cut and paste:
Goal: Convert an array of objects that contains duplicate objects (like this one)...
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
... Into an array of objects without duplicate objects (like this one):
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
Explanation provided in the comments:
var allContent = [{
"id": 10620,
"name": "Things to Print"
}, {
"id": 10620,
"name": "Things to Print"
}, {
"id": 4334,
"name": "Interesting"
}]
//Put Objects Into As Associative Array. Each key consists of a composite value generated by each set of values from the objects in allContent.
var noDupeObj = {} //Create an associative array. It will not accept duplicate keys.
for (i = 0, n = allContent.length; i < n; i++) {
var item = allContent[i]; //Store each object as a variable. This helps with clarity in the next line.
noDupeObj[item.id + "|" + item.name] = item; //This is the critical step.
//Here, you create an object within the associative array that has a key composed of the two values from the original object.
// Use a delimiter to not have foo+bar handled like fo+obar
//Since the associative array will not allow duplicate keys, and the keys are determined by the content, then all duplicate content are removed.
//The value assigned to each key is the original object which is along for the ride and used to reconstruct the list in the next step.
}
//Recontructs the list with only the unique objects left in the doDupeObj associative array
var i = 0;
var nonDuplicatedArray = [];
for (var item in noDupeObj) {
nonDuplicatedArray[i++] = noDupeObj[item]; //Populate the array with the values from the noDupeObj.
}
console.log(nonDuplicatedArray)
For those who love ES6 and short stuff, here it's one solution:
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" }
];
Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Jon" },
{ title: "cry", artist: "Jon" }
];
const unique = Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
console.log(`New array length: ${unique.length}`)
console.log(unique)
The above example only works for a unique title or id. Basically, it creates a new map for songs with duplicate titles.
Below code compares object with JSON as String format and removes duplicates and works fine with simple arrays.
Array.prototype.unique=function(a){
return function(){
return this.filter(a)
}
}(
function(a,b,c){
var tmp=[];
c.forEach(function(el){
tmp.push(JSON.stringify(el))
});
return tmp.indexOf(JSON.stringify(a),b+1)<0
})
If you are using underscore js, it is easy to remove duplicate object.
http://underscorejs.org/#uniq
function remove_duplicates(objectsArray) {
var arr = [], collection = [];
$.each(objectsArray, function (index, value) {
if ($.inArray(value.id, arr) == -1) {
arr.push(value.id);
collection.push(value);
}
});
return collection;
}
Using jQuery - I would like to compare 2 JSON arrays:
origArray comes from the database/C# API:
var origArray = [ {
"TypeName": "Single",
"TypeID": "3121",
"TypeCount": "2"
},
{
"TypeName": "Double",
"TypeID": "4056",
"TypeCount": "2"
},
{
"TypeName": "Family",
"TypeID": "5654",
"TypeCount": "4"
}
];
userArray is gathered from user input:
var userArray = [ {
"TypeID": "3121",
"TypeCount": "2"
},
{
"TypeID": "4056",
"TypeCount": "3"
},
{
"TypeID": "3121",
"TypeCount": "3"
}
];
What I would like to do, is loop through the userArray, and "group" by the TypeID, and Sum the TypeCount.
So in the example above:
TypeID: 3121
TypeCount: 5 (ie. 2 + 3)
TypeID: 4056
TypeCount: 3
Meaning there would be a difference of 3 over for TypeID 3121 and 1 over for 4056.
Is there any way of getting that information out of either jQuery or native Javascript (that would work cross-browser)?
Thanks for any help,
Mark
Define a function to group by each array by TypeID and sum TypeCount:
function groupByTypeID (arr) {
var groupBy = {};
$.each(arr, function () {
var currentCount = groupBy[this.TypeID] || 0;
groupBy[this.TypeID] = currentCount + parseInt(this.TypeCount);
});
return groupBy;
}
Then, group both arrays, and take their differences:
var userArrayGroups = groupByTypeID(userArray);
var origArrayGroups = groupByTypeID(origArray);
var diff = {};
for (var prop in userArrayGroups) {
diff[prop] = userArrayGroups[prop] - origArrayGroups[prop];
}
diff will then hold the following:
{
3121: 3
4056: 1
}
DEMO.
If you are familiar with C# this js library - linqjs.
It contains implementations of all .NET 4.0 LINQ methods and many extra methods.
You can do it with Underscore:
var result = _.chain(userArray)
.groupBy(function(value) {
// group by the TypeId
return value.TypeID;
})
.map(function(value) {
// iterate over every group
var addition = _.chain(value)
.pluck("TypeCount") // create an array with TypeCount values
.reduce(function(memo, num) {
return Number(memo) + Number(num); // add all the TypeCount values
})
.value();
return {
"TypeID": value[0].TypeID, // TypeID
"TypeCount": addition // total addition for this TypeID
};
})
.value();
Working example here: http://livecoding.io/3498441
I'll have a shot at it...
var mergeArray = []
var index;
for (var i = userArray.length - 1; i >= 0; i--) {
index = findByTypeID(mergeArray, userArray[i].TypeID);
if(!index){
mergeArray[index].TypeCount += parseInt(userArray[i].TypeCount)
} else{
mergeArray.push({
"TypeID": userArray[i].TypeID,
"TypeCount": parseInt(userArray[i].TypeCount)
});
}
};
function findByTypeID(arr, id){
for (var i = arr.length - 1; i >= 0; i--) {
if(arr[i].TypeID == id)
return i;
};
return -1;
}
This gives you your desired data structure output inside mergeArray
result = {};
for (var i in userArray) {
var elem = userArray[i]
if (!result[elem.TypeID])
result[elem.TypeID] = elem.TypeCount;
else
result[elem.TypeID] += elem.TypeCount;
}
return result;