I have this array:
[
{ id: "1", option { points: "1"} },
{ id: "2", option { points: "20"} },
{ id: "3", option { points: "4"} },
]
I'm trying to sum all the objectoption that have as property points, so I did:
var total_points = this.my_arr.reduce(function(a, b){
return a + b.option.points;
}, 0);
but this return a concatenation of the index of each id, something like: 012, that is of course wrong.
The expected output is: 25
You must convert your strings to numbers, for example:
var total_points = this.my_arr.reduce(function(a, b){
return a + parseInt(b.option.points);
}, 0);
Correct your object and cast the b as Number. In the object the values are strings so when + is used with string concatenations happens not addition. That's why the strings are converted to numbers and then added
var e=[
{ id: "1", option :{ points: "1"} },
{ id: "2", option :{ points: "20"} },
{ id: "3", option :{ points: "4"} },
]
var total_points = e.reduce(function(a, b){
return a + Number(b.option.points);
}, 0);
console.log(total_points);
In your object options is string when you use + operator b/w two strings it joins both of then. Use you convert string to number using parseInt() or Number()
var total_points = this.my_arr.reduce(function(a, b){
return a + parseInt(b.option.points);
}, 0);
You must convert the string to a number, you can use parseInt for that, or even simpler prepend a + before the string.
This version works with NaN, undefined or missing properties:
const data = [
{ id: "1", option: { points: "1" } },
{ id: "2", option: { points: "20" } },
{ id: "3", option: { points: "4" } },
{ id: "4", option: { points: NaN } },
{ id: "5", option: { points: undefined } },
{ id: "6", option: { } },
{ id: "7" },
];
const totalPoints = data.reduce((accum, elem) => {
const value = elem.option && elem.option.points ? +elem.option.points : 0;
return accum + value;
}, 0);
console.log(totalPoints);
Related
I have the following issue I have a set of JSON rules like so
{
"event": {
"type": "maxrulecount",
"params": {
"maxrulecount": 2
}
},
"conditions": {
"any": [
{
"all": [
{
"fact": "apples",
"value": "20",
"operator": "greaterThanInclusive"
}
]
},
{
"all": [
{
"fact": "bananas",
"value": "100",
"operator": "greaterThanInclusive"
}
]
}
]
}
}
So i obviously convert this to an object but the number value remains a string so I have created a function that will convert any numbers that are strings into numbers like so
checkForNumberValues(rules) {
// allows any number of numbers together or a decimal number
let numberRegex = /^(([0-9]{1,})|([0-9]{1,}\.[0-9]{1,}))$/g;
// yes a loop within a loop but time complexity is no issue here
rules?.conditions?.any?.forEach((condition) => {
condition?.all?.forEach((rule) => {
console.log(rule.value, numberRegex.test(rule.value)); // this is working correctly
if (numberRegex.test(rule.value)) {
rule.value = Number(rule.value);
}
});
});
console.log(rules);
return rules;
}
now i can see that it is correctly identifying numbers and setting the value but when i console the result like so
console.log(checkForNumberValues(rules));
I'ts returning the rules object with the string number values instead of the number values I set..
Do I need to do something special to set nested values??
Below is an example
let rules = {
conditions: {
any: [
{
all: [
{ fact: 'orange', value: '70' },
{ fact: 'apple', value: '10' },
{ fact: 'brocolli', value: '54' },
{ fact: 'kiwi fruit', value: '199' }
]
}
]
}
}
function checkForNumberValues(rules) {
let numberRegex = /^(([0-9]{1,})|([0-9]{1,}\.[0-9]{1,}))$/g;
rules.conditions.any.forEach((condition) => {
condition.all.forEach((rule) => {
if (numberRegex.test(rule.value)) {
rule.value = Number(rule.value);
}
})
});
return rules;
}
console.log(checkForNumberValues(rules));
Any help would be appreciated!
Regexp "remembers" the last index where a match was found when the global flag g is used (-> Why does a RegExp with global flag give wrong results?)
Use parseInt()/Number() and then test for NaN
let rules = {
conditions: {
any: [
{
all: [
{ fact: 'orange', value: '70' },
{ fact: 'apple', value: '10' }
]
}
]
}
}
function checkForNumberValues(rules) {
rules.conditions.any.forEach((condition) => {
condition.all.forEach((rule) => {
const val = parseInt(rule.value);
if (!isNaN(val)) {
rule.value = val;
}
})
});
return rules;
}
console.log(checkForNumberValues(rules));
Use isNaN to check if the string is a number or not.
let rules = {
conditions: {
any: [
{
all: [
{ fact: 'orange', value: '70' },
{ fact: 'apple', value: '10' },
{ fact: 'brocolli', value: '54' },
{ fact: 'kiwi fruit', value: '199' }
]
}
]
}
}
function checkForNumberValues(rules) {
rules.conditions.any.forEach((condition) => {
condition.all.forEach((rule) => {
if (!isNaN(rule.value)) {
rule.value = Number(rule.value);
}
})
});
return rules;
}
console.log(checkForNumberValues(rules));
you can try a different approach
let rules = {
conditions: {
any: [
{
all: [
{ fact: 'orange', value: '70' },
{ fact: 'apple', value: '10' }
]
}
]
}
}
rules.conditions.any.filter(x=>{(x.all).forEach(x=>x.value=parseInt(x.value))})
console.log(rules)
I have a problem in 'getObjectById to using in recursion'. Executing the function getObject(data, '11') ---> undefined. I don't know why this happens: undefined.
In function getObject(data, '1'~'9') ---> I got the solved. But '11' ,'12', '13', '14' ---> undefined
For solving this problem, I have to use forEach, Array.prototype.apply, but I cannot solve it.
Using filter ---> TypeError: Cannot read property 'filter' of undefined
Using length ---> TypeError: Cannot read property 'length' of undefined
In first under line, I have got the problem to solve. In second under line, I wrote the code to solve this problem. As I think that I explain my logic to solving the problem. But, in test case, it is failed.
The solution to this problem is:
let output = getObjectById(TREE_DATA.items, '1'))
console.log(output) --> { "id": "1", "name": "johnny" }
--in first under line,
let TREE_DATA = {
items: [
{
id: "1",
name: "johnny"
},
{
id: "2",
name: "ingi",
children: [
{
id: "3",
name: "johnson"
},
{
id: "4",
name: "katy"
},
{
id: "5",
name: "steve",
children: [
{
id: "6",
name: "lisa"
},
{
id: "7",
name: "penny",
children: [
{
id: "8",
name: "john"
},
{
id: "9",
name: "hoyong"
}
]
},
{
id: "10"
}
]
},
{
id: "11"
},
{
id: "12"
}
]
},
{
id: "13"
},
{
id: "14"
}
]
};
--in second under line,
function getObject(json, id) {
let test = json.items;
let newA = [];
function getA(a, id) {
a.filter(function(e) {
console.log("this is : ", e);
if (e.id && e.id === id) {
return newA.push(e);
} else if (e.id !== id && e.children) {
return getA(e.children, id);
}
});
}
getA(test, id);
return newA[0];
}
Because your input data is a recursive structure, a program with a recursive structure will be the best match. In this case you have -
a list of nodes (TREE_DATA.items)
where each node (object) may contain a children property, which is also a list of nodes
This recursive relationship gives us a unique opportunity to learn about special kind of recursion where one function, A, calls function B, which in turn calls function A, which calls B, and so on... this is called mutual recursion.
We start with a function that takes just one of the input nodes, aptly named find1. It accepts a single node, destructured to children and o, and an id to search for, id -
const find1 = ({ children = [], ...o }, id = 0) =>
o.id == id // if the object's id matches the input id,
? o // match found! return the object
: findAll(children, id) // otherwise findAll of the children with the id
Next it's obvious we need to implement findAll. It accepts a list of nodes, destructured to first and more, and an id to search for, id -
const findAll = ([ first, ...more ], id = 0) =>
first === undefined // if the list is empty,
? undefined // there's nothing to search! return no match
: find1(first, id) // find1 the first item in the list using the id
|| findAll(more, id) // OR findAll on more using the same id
That's it! The functions almost writes themselves with no need for extraneous variables or steps. It behaves exactly like we expect -
console.log(findAll(TREE_DATA.items, 1))
// { id: "1", name: "johnny" }
console.log(findAll(TREE_DATA.items, 11))
// { id: "11" }
console.log(findAll(TREE_DATA.items, 99))
// undefined (no match found)
Verify the results in your own browser by running the snippet below -
const find1 = ({ children = [], ...o }, id = 0) =>
o.id == id
? o
: findAll(children, id)
const findAll = ([ first, ...more ], id = 0) =>
first === undefined
? undefined
: find1(first, id) || findAll(more, id)
const TREE_DATA =
{items:[{id:"1",name:"johnny"},{id:"2",name:"ingi",children:[{id:"3",name:"johnson"},{id:"4",name:"katy"},{id:"5",name:"steve",children:[{id:"6",name:"lisa"},{id:"7",name:"penny",children:[{id:"8",name:"john"},{id:"9",name:"hoyong"}]},{id:"10"}]},{id:"11"},{id:"12"}]},{id:"13"},{id:"14"}]}
console.log(findAll(TREE_DATA.items, 1))
// { id: "1", name: "johnny" }
console.log(findAll(TREE_DATA.items, 11))
// { id: "11" }
console.log(findAll(TREE_DATA.items, 99))
// undefined (no match found)
Is this what you are looking for?
let TREE_DATA = {
items: [
{
id: "1",
name: "johnny"
},
{
id: "2",
name: "ingi",
children: [
{
id: "3",
name: "johnson"
},
{
id: "4",
name: "katy"
},
{
id: "5",
name: "steve",
children: [
{
id: "6",
name: "lisa"
},
{
id: "7",
name: "penny",
children: [
{
id: "8",
name: "john"
},
{
id: "9",
name: "hoyong"
}
]
},
{
id: "10"
}
]
},
{
id: "11"
},
{
id: "12"
}
]
},
{
id: "13"
},
{
id: "14"
}
]
};
function getObject(json, id) {
let test = json.items;
let newA = [];
function getA(a, id) {
a &&
a.forEach(function(e) {
if (e.id === id) {
newA.push(e);
} else if (e.children) {
getA(e.children, id);
}
});
}
getA(test, id);
return newA[0];
}
function getObjectById(items, key) {
let ret = {};
for (let item of items) {
if (item.id === key) {
return item;
}
if (item.children) {
let innerRet = getObjectById(item.children, key);
if (Object.keys(innerRet).length) return innerRet;
}
}
return ret;
}
console.log("2", getObjectById(TREE_DATA.items, "2"));
console.log("3", getObjectById(TREE_DATA.items, "3"));
console.log("11", getObjectById(TREE_DATA.items, "11"));
console.log("12", getObjectById(TREE_DATA.items, "12"));
console.log("13", getObjectById(TREE_DATA.items, "13"));
console.log("2", getObject(TREE_DATA, "2"));
console.log("3", getObject(TREE_DATA, "3"));
console.log("11", getObject(TREE_DATA, "11"));
console.log("12", getObject(TREE_DATA, "12"));
console.log("13", getObject(TREE_DATA, "13"));
Edit: debugged and added your function as well.
This is my original Object:
Data = {
aesthetic: {
heritage: 'aesthetic',
description: 'sdokjosk',
value: 5
},
architectural: {
heritage: 'architectural',
description: 'doodsjdj',
value: 1
},
historical: {
heritage: 'historical',
description: 'dcnsdlnckdjsncksdjbk kjdsbcjisdc hsdk chjsd cjhds ',
value: 4
},
score: 3
};
i want to write a function that goes through the object and converts the properties only with 'heritage' 'description' and 'value' into an array that would look like this:
[{
"heritage": "aesthetic",
"description": "sdokjosk",
"value": 5
},
{
"heritage": "architectural",
"description": "doodsjdj",
"value": 1
},
{
"heritage": "historical",
"description": "dcnsdlnckdjsncksdjbk kjdsbcjisdc hsdk chjsd cjhds ",
"value": 4
}
]
This is what I have tried:
Object.values(Data)
but this returns an array with the 3 from the score property at the end
You can use Object.values() method to get the array of values, then filter only objects from it using Array#filter() method.
This is how should be your code:
var result = Object.values(Data).filter(x => typeof x == 'object');
Demo:
This is a working demo snippet:
var Data = {
aesthetic: {
heritage: 'aesthetic',
description: 'sdokjosk',
value: 5
},
architectural: {
heritage: 'architectural',
description: 'doodsjdj',
value: 1
},
historical: {
heritage: 'historical',
description: 'dcnsdlnckdjsncksdjbk kjdsbcjisdc hsdk chjsd cjhds ',
value: 4
},
score: 3
};
var result = Object.values(Data).filter(x => typeof x == 'object');
console.log(result);
If you want to check all the keys you mentioned should present you can go with this :
Object.values(Data).filter(function(data){return data["heritage"] && data["description"] && data["value"]})
Following is my code to get the languages with Language id and language text
for (var p in $scope.bulk.Langugaes) {
$scope.lsLanguagewithTextndValue.push($scope.bulk.Langugaes[p].Value, $scope.bulk.Langugaes[p].Text);
}
but for above code the value in lsLanguagewithTextndValue
0:"1"
1:"Marathi"
2:"2"
3:"English"
4:"4"
5:"Hindi"
6:"3"
7:"French"
But I want output like this
1:Marathi
2:English
3.Hindi
4.French
$scope.lsLanguagewithTextndValue.push({ $scope.bulk.Langugaes[p].Value: $scope.bulk.Langugaes[p].Text });
Multiple arguments in .push just pushes each argument in to the array.
If you want to add a pair key - value do it like this:
obj[key] = value;
In your case it should be something like this:
for (var p in $scope.bulk.Langugaes) {
$scope.lsLanguagewithTextndValue[$scope.bulk.Langugaes[p].Value] = $scope.bulk.Langugaes[p].Text;
}
Try this.
const $scope = {
bulk: {
Languages: {
ln1: { value: 1, text: 'Marathi' },
ln2: { value: 2, text: 'English' },
ln3: { value: 3, text: 'Hindi' },
ln4: { value: 4, text: 'French' }
}
},
lsLanguagewithTextndValue: []
}
// just to make it more readable
const langs = $scope.bulk.Languages;
for (let p in langs) {
$scope.lsLanguagewithTextndValue.push({[langs[p].value]: langs[p].text})
}
console.log($scope.lsLanguagewithTextndValue);
In this case use map Array map. This function make a new array with elements of another.
$scope.lsLanguagewithTextndValue =
$scope.bulk.Langugaes.map((langugaes) => {
// langugaes its a element of $scope.bulk.Langugaes for example
// $scope.bulk.Langugaes[p]
return {langugaes.Value: langugaes.Text}
})
Result:
{
"1": "Marathi"
},
{
"2": "English"
},
{
"3": "Hindi"
},
{
"4": "French"
}
I have been struggling with this problem the whole day.
I have got the following array:
controller.array = [{
Equity: "0",
Bond: "97.42",
Cash: "67.98"
}, {
Equity: "5.32",
Bond: "13.12",
Cash: "8"
}, {
// ...
} /* ... */ ]
What I want to do is create a single array containing objects with combined values like so:
controller.newArray = [{
Type: "Equity",
Percentage: "5.32"
}, {
Type: "Bond",
Percentage: "110.54"
}, {
Type: "Cash",
Percentage: "75.98"
} /* ... */ ]
I have tried using _.each like this:
.map(function(item, value) {
var array = [];
_.each(item, function(value, item) {
array.push({
'Source': item,
'Percentage': value
})
})
return array;
})
.value()
What then happens is that it returns an array, containing multiple arrays with objects with my values. Now my problem is that I cant seem to combine all the arrays that are being returned.
Any ideas? Please?
You can transpose the array of objects into an array of values grouped by their common key.
Then you can map the values over to the resulting objects.
The transpose() and sum() functions are underscore mixins, so you can chain them!
_.mixin({
transpose : function(array) {
return _.chain(array).map(_.keys).flatten().uniq().reduce(function(result, key) {
result[key] = _.pluck(array, key);
return result;
}, {}).value();
},
sum : function(values) {
return _.reduce(values, function(sum, value) {
return sum + (_.isNumber(value) ? value : parseFloat(value));
}, 0);
}
});
var array = [{
Equity: "0",
Bond: "97.42",
Cash: "67.98"
}, {
Equity: "5.32",
Bond: "13.12",
Cash: "8"
}];
var result = _.chain(array).transpose().map(function(value, key) {
return {
Type: key,
Percentage: _.sum(value).toFixed(2)
};
}).value();
console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
One way, using only JavaScript could be to use map and reduce functions:
var data = [{Equity: "0", Bond: "97.42", Cash: "67.98"},
{Equity: "5.32", Bond: "13.12", Cash: "8"}];
var sumMap = data.reduce(function(acc, item) {
Object.keys(item).forEach(function(itemKey) {
if (acc[itemKey] === undefined) {
acc[itemKey] = 0;
}
acc[itemKey] += parseFloat(item[itemKey]);
});
return acc;
}, {});
var result = Object.keys(sumMap).map(function(itemKey) {
return {
"Type": itemKey,
"Percentage": "" + sumMap[itemKey]
};
});
console.log(JSON.stringify(result, null, 2));
The intermediate result sumMap will be something like this:
{
Equity: 5.32,
Bond: 110.54,
Cash: 75.98
}
The fiddle (thanks to CPHPython).
If what you need is to sum each type then this should do:
keys = {Equity: 0, Bond: 0, Cash: 0}
//newArray is the array from your question
newArray.forEach(function(obj) {
keys[obj.Type] += obj.Percentage})