Iterating through object to add up all matching properties - javascript

I have a caml query which is going to return something like this in xml.
ID Title Percentage
7;#7 2 1.00000000000000
7;#7 3 0.220000000000000
7;#7 sub 1.1 0
7;#7 4 0.140000000000000
12;#12 7 0.670000000000000
13;#13 6 0.700000000000000
I'll likely create an aray of objects for each item. Something like this:
var result = [{id:7,title:"2",percent:1.0},...,{id:13,title:"6",percent:0.7}]
How could I iterate through the result and add up all the percentages with the same ID so I end up with something like:
var total = [{id:7,percent:1.36,count:4},{id:12,percent:0.67,count:1},{id:13,percent:0.7,count:1}]
Or even if I could just get
percent/count = totalPercentage so I end up with an object with just {id:7,totalPercentage:0.325}

Try this:
var percentages = {};
result.forEach(function (it) {
var obj = percentages[it.id] = percentages[it.id] || {
percent: 0,
count: 0,
id: it.id
};
obj.percent += Number(it.percent); // Casting to Number, in case percent comes as string
obj.count++;
});
This creates an object, with ids as keys. Should you wish to convert it to an array:
total = Object.keys(percentages).map(function (it) {
return percentages[it]
});
To get the average of the percentages, you can do this:
total = total.map(function(it) {
return {
id: it.id,
percent: it.percent / it.count
};
});

Just make the new object and iterate though the old one. You can see a demo here: http://jsfiddle.net/TSyE5/2/
var totalPercent = [],
total = {},
result = [{id:8,title:"3",percent:1.0},{id:7,title:"2",percent:1.0},{id:7,title:"2",percent:3.0},{id:13,title:"6",percent:0.7},{id:13,title:"6",percent:0.7},{id:13,title:"6",percent:0.7}];
$.each(result, function(){
!(this.id in total) && (total[this.id] = {id:this.id, title:this.title, percent:0, count:0});
total[this.id].percent += this.percent;
total[this.id].count++;
});
$.each(total, function(i){
total[i].percent = total[i].percent/total[i].count;
totalPercent.push({id:total[i].id, percent:total[i].percent});
});

Related

How do I search a string in JavaScript array using jQuery? [duplicate]

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 6 years ago.
I have a JavaScript array:
var j_array = new Array();
j_arry=["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"];
I need to find how many times the class is coming and its array key, so I use:
found = $.inArray('class', j_array); ` But it returns `-1`;
Then I use:
var search = 'class';
$.each([j_array], function(index, value){
$.each(value, function(key, cell){
if (search.indexOf(cell) !== -1)
console.log('found in array '+index, cell);
});
});
But that is also wrong. How do I solve this?
From this array I want to get the following:
Class coming 4 times, at key 0, 2, 3, and 7
I want to make a separate array of class only, that is,
new_array = ["class:1", "class:2", "class:3", "class:10"];
Currently there are four classes in j_array. How can I get the Nth class value
That is, 1st class value ="class:1", 2nd class value="class:5", etc.
You could filter elements which match in a new array and just return the length of this new array
var j_arry = ["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"];
var res = j_arry.filter(x => x.includes("class"));
var key = res.map(x => x.split(":")[1]);
console.log("Class coming " + res.length + " , at key " + key.join(","));
console.log("new array = ", res);
Use Array.prototype.filter to filter out the elements of the array that contains the string class - see demo below:
var j_array =["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"];
var result = j_array.filter(function(e){
return e.indexOf('class')!==-1;
});
console.log(result);
EDIT:
To get the list of indexes too, you can try this:
var j_array =["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"];
var filteredIndices = []
var filtered = j_array.filter(function(e,i){
if(e.indexOf('class')!==-1) {
filteredIndices.push(i);
return true;
} else {
return false;
}
});
console.log(filtered);
console.log(filteredIndices);
// Nth class value
console.log(filtered[2]); // this prints the 3rd one
.as-console-wrapper{top:0;max-height:100%!important;}
Here is the answer to your questions 1 + 2. It is also 'n' proof so answers your part 3 also. This works by old-fashioned hard graft rather than funky functions. The original array entries are split and filtered then if qualifying we store in an associative array (results) using a pointer array (list) to make it easier to give a sorted result and pull the values from the associative array. The max variable is probably not necessary but included for clarity - could have used list.length instead. Note that the list[] array will be sparse (missing steps) so we test each entry before use in the output steps.
var j_array = new Array();
j_arry=["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10","class:1"];
var a, result = [], list=[], max = -1
for (var i =0; i < j_arry.length; i = i + 1) {
var a = j_arry[i].split(":")
if ( a[0] === "class") {
var key = "c" + a[1]
if ( !result[key] ) { result[key] = {pos:[]}}
result[key].cnt = result[key].cnt ? result[key].cnt + 1 : 1;
result[key].pos.push(i)
list[parseInt(a[1])] = "c" + a[1]
max = parseInt(a[1]) > max ? a[1] : max;
}
}
// say locations
for (var i = 0; i < max; i = i + 1) {
if (list[i]) {
key = "c" + i
console.log("Class " + i + " occurs at " + result[key].pos.toString() )
}
}
// make new array
var newArray=[]
for (var i = 0; i < max; i = i + 1) {
if (list[i]) {
newArray.push("Class:" + i)
}
}
console.log("New array=" + newArray.toString() )
Results are:
Class 1 occurs at 0,8
Class 3 occurs at 3
Class 5 occurs at 2
New array=Class:1,Class:3,Class:5
Single reduce is sufficient here.
var arr = ["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"],
res = arr.reduce((p,c) => c.includes("class") ? (p.count++, p.keys.push(c.split(":")[1]), p)
: p ,{count:0, keys:[]});
console.log(res);
You can use the filter and map functions to filter your array to have only elements that match the text 'class', and use array index notation to access the nth element in the array. Check the below code snippet I hope it will be of help to you.
The below code snippet uses ES6 arrow syntax.
var arr = ["class:1", "division:a", "class:5", "class:3", "division:b", "division:c", "division:d", "class:10"];
var result = arr.filter(x => x.indexOf('class') !== -1);
var indices = result.map(x => arr.indexOf(x));
console.log(indices);
console.log(result);
var nValue = window.prompt('Enter n value');
console.log(result[nValue]);
If you're using jQuery to support some really old browser that still don't implement the new Array functions, and you don't want to polyfill those because you're already using jQuery, then you can use the jQuery equivalents:
var arr = ["class:1", "division:a", "class:5", "class:3", "division:b", "division:c", "division:d", "class:10"]
var result = $.grep(arr, function (x) { return x.indexOf('class') !== -1 })
var indices = $.map(result, function (x) { return arr.indexOf(x) })
This is the same code as this answer, but using jQuery.
You have to do map first then filter.
var j_array = ["class:1", "division:a", "class:5", "class:3", "division:b", "division:c", "division:d", "class:10"];
var result = j_array.map(function(e, i) {
return e.indexOf('class') > -1 ? '' + i : false;
}).filter(function(e) {
return !!e;
});
console.log(result);

How to add same id quantity in below arrays using javascript?

I have two arrays which are
product_id = [5,5,10,15,5,15,22]
product_qty = [58,40,120,100,98,100,50]
(I have stored to array as sequence from table. id and quantity orders are same as i mentioned above.)
I want to calculate the same id's total quantity with their id. Result should be
result_id = [5,10,15,22] //no duplicates
result_qty = [196,120,200,50] //sum to same id's quantity
how to solve this issue in javascript?
One possible solution (keeping the two array solution as specified in the question, although you may want to look into a hash as specified by Vineswaran in the comments) is to traverse the first array (with the ids) and push the value if it doesn't exist in the index array, or add the value if it exists in the index array, like this:
var product_id = [5,5,10,15,5,15,22];
var product_qty = [58,40,120,100,98,100,50];
var result_id = [];
var result_qty = [];
// traverse the product ids array
$.each(product_id, function(idx, val) {
// check if that product id had a previous instance
var auxIdx = $.inArray(val, result_id)
if (auxIdx >= 0) {
// if it did, add the quantities
result_qty[auxIdx] += product_qty[idx];
} else {
// if it didn't, push both id and quantity into the result arrays
result_id.push(val);
result_qty.push(product_qty[idx]);
}
});
console.log(result_id);
console.log(result_qty);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
You can see it on this JSFiddle too: http://jsfiddle.net/pmkLcLdd/
I don't have your sample data. So, i have modified your code in the comment.
var store_product = {};
$(data).each(function(did,value){
var product_place_id = value.product_place_id_primary;
var ids = getProductAndPlaceId(product_place_id);
var item_quantity = store_product[ids[0]];
if (item_quantity) {
store_product[ids[0]] = (item_quantity + value.quantity);
} else {
store_product[ids[0]] = value.quantity;
}
});
store_product hash will have your expected result. You can convert it into array or anything as per your needs.
you don't need jquery for this
var product_id = [5,5,10,15,5,15,22]
var product_qty = [58,40,120,100,98,100,50]
var result_id = product_id.slice(); //Copy arrays
var result_qty = product_qty.slice()
var i = result_id.length;
while(i > 0){
var id = result_id.shift(); //remove first element
--i;
var sum = result_qty.shift(); //init sum count
var index = result_id.indexOf(id, 0); //find next match of element
while(index != -1){
result_id.splice(index, 1); //remove element in index
--i;
sum += result_qty.splice(index, 1)[0]; //index 0, since splice return a list with length 1
index = result_id.indexOf(id,index);
}
result_id.push(id); //add to end
result_qty.push(sum);
}
console.log(result_id);
console.log(result_qty);
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
I think an object would be better suited for what you want as a result, this way, you can easily relate an id to its quantity, with the id being the key:
var product_id = [5,5,10,15,5,15,22],
product_qty = [58,40,120,100,98,100,50],
result_qty = {};
product_qty.forEach(function (qty, i) {
var indexVal = result_qty[product_id[i]] || 0;
result_qty[product_id[i]] = indexVal + qty;
});
console.log(result_qty);
// Logs Object {5: 196, 10: 120, 15: 200, 22: 50}
console.log(result_qty[5]);
// Logs 196
//////////////////////////////////////////////////////////////////
// * However, if you really want both, the ids and quantities in
// * array format, then it's just a matter of running this,
// * after the previous code:
var tmp = [],
result_id = [];
// * (looping through the object)
for(var prop in result_qty) {
if(result_qty.hasOwnProperty(prop)) {
tmp.push(result_qty[prop]);
result_id.push(parseInt(prop, 10));
}
}
result_qty = tmp;
console.log(result_id);
// Logs [196, 120, 200, 50]
console.log(result_qty);
// Logs [5, 10, 15, 22]
I've included a way to get the arrays anyway, so you have both options.

Check if an object with index is in array

$.each(constructions, function(i,v) {
if ($.inArray(v.name, map[ii].buildings) == -1) {//stuff}
};
Where constructions is an array of objects, each with a unique name. map[ii].buildings is an array containing some of these objects. I want to iterate each object in constructions, checking if its name parameter appears in the objects of map[ii].buildings.
The above code works if the each element in the map[ii].buildings array is just the text string of the object name, but not if the element is the entire object.. close, but no dice >.<
Try using $.grep() instead of $.inArray(); you can specify a function to do the filtering for you.
Instead of checking for -1, you check whether the array that $.grep() returns has length == 0
Simple example: (would be easier if you posted the code / example of what "constructions" objects look like)
var constructions = [{
Name: "Mess hall",
SqFt: 5000
}, {
Name: "Infirmary",
SqFt: 2000
}, {
Name: "Bungalow",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
var buildings = [{
Name: "Infirmary",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
// found buildings will be list of items in "constructions" that is not in "buildings"
var foundBuildings = $.grep(constructions, function (constructionsItem) {
return $.grep(buildings, function (buildingsItem) {
return buildingsItem.Name === constructionsItem.Name
}).length == 0; // == 0 means "not in", and > 0 means "in"
});
// this just renders the results all pretty for ya
$.each(foundBuildings, function (idx, item) {
$("#output").append("<div>" + item.Name + "</div>");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='output'></div>
Example jsFiddle: http://jsfiddle.net/eLeuy9eg/3/
The non-jQuery way of doing this would be to use filter. Something like this:
// pass in an array and the key for which you want values
// it returns an array of those values
function getValues(arr, key) {
return arr.map(function (el) { return el[key]; });
}
function notFoundIn(arr, arr2) {
// grab the names of the buildings
var buildings = getValues(arr2, 'name');
// grab the names from the construction objects and filter
// those that are not in the building array
return getValues(arr, 'name').filter(function (el) {
return buildings.indexOf(el) === -1;
});
}
notFoundIn(constructions, buildings); // eg [ "one", "three" ]
DEMO
You could even add a new method to the array prototype. With this one you can use either simple arrays, or arrays of objects if you pass in a key. Note in this example I've replaced map and filter with loops that perform the same functions, but faster (see comments):
function getValues(arr, key) {
var out = [];
for (var i = 0, l = arr.length; i < l; i++) {
out.push(arr[i][key]);
}
return out;
}
if (!Array.prototype.notFoundIn) {
Array.prototype.notFoundIn = function (inThisArray, key) {
var thisArr = key ? getValues(this, key) : this;
var arrIn = key ? getValues(inThisArray, key) : inThisArray;
var out = [];
for (var i = 0, l = thisArr.length; i < l; i++) {
if (arrIn.indexOf(thisArr[i]) === -1) {
out.push(thisArr[i]);
}
}
return out;
}
}
constructions.notFoundIn(buildings, 'name');
[1, 2, 3].notFoundIn([2]); // [1, 3]
DEMO

Combining results into one object

I'm looping through a set of inputs. I need to tally up the grouped totals. The inputs below to one of three categories.
How do I go about combining the values up relevant to three categories?
var compoundedArray = new Array();
holder.find(".dataset input").each(function(index) {
var val = $(this).val();
var dataType = $(this).data("type");
var localObj = {};
localObj[dataType] = val;
compoundedArray.push(localObj);
});
I have an object like this
[
{
"growth":30
},
{
"growth": 40
},
{
"other": 20
}
]
how do I loop through the object to produce something like
[
{
"growth": 70
},
{
"other": 20
}
]
if I looped over the initial array object
for (var i = 0; i < compoundedArray.length; i++) {
console.log(compoundedArray[i]);
}
how would I go about checking to ensure I don't have duplicates - and that I can tally up the results?
Ideally the resulting format may be the best
var array = [
"matching": 50,
"growth": 20
]
var array = [
"matching": 50,
"growth": 20
]
is not valid JS, but you can create an object of the form
var obj = {
"matching": 50,
"growth": 20
};
And that's pretty easy to do, just use an object from the very beginning:
var result = {};
holder.find(".dataset input").each(function(index) {
var val = +$(this).val(); // use unary plus to convert to number
var dataType = $(this).data("type");
result[dataType] = (result[dataType] || 0) + val;
});
Further reading material:
MDN - Working with Objects
Eloquent JavaScript - Data structures: Objects and Arrays
You can just use an object (not array) with unique keys.
var compoundedObj = {};
$(".dataset input", holder).each(function() {
var dataType = $(this).data("type");
if(!compoundedObj.hasOwnProperty(dataType)) {
compoundedObj[dataType] = 0;
}
compoundedObj[dataType] += parseInt($(this).val(), 10);
});
In this way you'll get an object like this:
{
"growth": 70,
"other": 20
}
Live demo
http://jsfiddle.net/GFwGU/
var original = [{"growth":30},{"growth": 40},{"other": 20}]
// object to sum all parts by key
var sums = {}
// loop through original object
for(var index in original){
// get reference to array value (target object)
var outer = original[index]
// loop through keys of target object
for(var key in outer){
// get a reference to the value
var value = outer[key]
// set or add to the value on the sums object
sums[key] = sums[key] ? sums[key] + value : value
}
}
// create the output array
var updated = []
// loop through all the summed keys
for(var key in sums){
// get reference to value
var value = sums[key]
// create empty object
var dummy = {}
// build object into desired format
dummy[key] = value
// push to output array
updated.push(dummy)
}
// check the results
alert(JSON.stringify( updated ))
var add=function (a,b){ a=a||0; b=b||0; return a+b};
var input=[ {growth:30},{growth:40},{other:20} ],output=[],temp={};
$.each(input,function(i,o){
var n;
for(i in o)
{n=i;break}
temp[n]=add(temp[n],o[n]);
});
$.each(temp,function(i,o){
var k={};
k[i]=o;
output.push(k)
});

Create child objects from parent with same property values [duplicate]

This question already has answers here:
How to put items into grouped arrays where grouped by a particular key
(3 answers)
Closed 9 years ago.
I have a parent object. I want to create child objects from the parent with the same key value pair.
e.g.
parentJSON = {[name:"a1",address:"b1",comp:"c1"],
[name:"a2",address:"b2",comp:"c1"],
[name:"a3",address:"b3",comp:"c2"],
[name:"a4",address:"b4",comp:"c2"],
[name:"a5",address:"b5",comp:"c2"],
[name:"a6",address:"b6",comp:"c3"]}
Now I want to create child objects having same "comp" value.
e.g.
childJSON1 = {[name:"a1",address:"b1",comp:"c1"],
[name:"a2",address:"b2",comp:"c1"]}
childJSON2 = {[name:"a3",address:"b3",comp:"c2"],
[name:"a4",address:"b4",comp:"c2"],
[name:"a5",address:"b5",comp:"c2"]}
childJSON3 = {[name:"a6",address:"b6",comp:"c3"]}
This is what I tried to make it little bit (it will change the parent object with a key indicating number of repetition):
parentJSON = [1,2,3,3,4,4,4,5];
var i=0, x, count, item;
while(i < parentJSON.length) {
count = 1;
item = parentJSON[i];
x = i+1;
while(x < parentJSON.length &&
(x = parentJSON.indexOf(item, x)) != -1) {
count += 1;
parentJSON.splice(x,1);
}
parentJSON[i] = new Array(parentJSON[i],count);
++i;
}
console.log(parentJSON);`
first of all your json is in the incorrect format, it should look like this
[{name:"a1",address:"b1",comp:"c1"},
{name:"a2",address:"b2",comp:"c1"},
{name:"a3",address:"b3",comp:"c2"},
{name:"a4",address:"b4",comp:"c2"},
{name:"a5",address:"b5",comp:"c2"},
{name:"a6",address:"b6",comp:"c3"}]
An array of objects.
My attempt, also very readable.
var result = {};
$.each(parentJSON, function (i, item) {
if(!result[item.comp]) {
result[item.comp] = [];
}
(result[item.comp]).push(item);
});
alert(JSON.stringify(result))
JsFiddle
First of all your json is actually invalid. You may have an array of objects, but not object which contains an array like that. Also your arrays looks more like objects, because the syntax with the dots is used for objects. Here is how I guess should look like:
var parentJSON = [
[{name:"a1",address:"b1",comp:"c1"}],
[{name:"a2",address:"b2",comp:"c1"}],
[{name:"a3",address:"b3",comp:"c2"}],
[{name:"a4",address:"b4",comp:"c2"}],
[{name:"a5",address:"b5",comp:"c2"}],
[{name:"a6",address:"b6",comp:"c3"}]
];
var child1 = parentJSON.slice(0, 2);
var child2 = parentJSON.slice(2, 5);
And you may use the .slice method to get specific elements of the array.
So..you need to clone objects?
maybe tou can try sth like this:
var sergi= {
name: "sergi",
age: 33
};
var bill = (JSON.parse(JSON.stringify(sergi)));
bill.name = "Bill";
console.log(sergi);
console.log(bill);
parentJSON = function(){
return [
{name:"a1",address:"b1",comp:"c1"},
{name:"a2",address:"b2",comp:"c1"},
{name:"a3",address:"b3",comp:"c2"},
{name:"a4",address:"b4",comp:"c2"},
{name:"a5",address:"b5",comp:"c2"},
{name:"a6",address:"b6",comp:"c3"}
];
}
childJSON1 = new parentJSON().slice(0,2);
childJSON2 = new parentJSON().slice(2,5);
childJSON3 = new parentJSON().slice(5,6);
Try this:
DEMO
var data = [
[{name:"a1",address:"b1",comp:"c1"}],
[{name:"a2",address:"b2",comp:"c1"}],
[{name:"a3",address:"b3",comp:"c2"}],
[{name:"a4",address:"b4",comp:"c2"}],
[{name:"a5",address:"b5",comp:"c2"}],
[{name:"a6",address:"b6",comp:"c3"}]
];
var groups = {};
$.each(data, function(i, item) {
var comp = item.comp;
delete item.comp;
if(groups[comp]) {
groups[comp].push(item);
} else {
groups[comp] = [item];
}
});
var result = $.map(data, function(group, key) {
var obj = {};
obj[key] = group;
return obj;
});
alert(JSON.stringify(groups))

Categories