Getting count of object based on condition - javascript

I have an object containing a bunch of similar objects. I would like to get the count of the object only for those where a object property (status) is of a given value (true). For instance, the count of the below object is 3.
{
6:{"name":"Mary", "status":true},
2:{"name":"Mike", "status":true},
1:{"name":"John", "status":false},
4:{"name":"Mark", "status":true},
5:{"name":"Jane", "status":false}
}
Thanks

I recognize you are iterating over an object, not an array, but since the others provide solutions for arrays I recon a solution with array.reduce is in place. Works in most modern browsers (IE9+)
var myArray = [
{"name":"Mary", "status":true},
{"name":"Mike", "status":true},
{"name":"John", "status":false},
{"name":"Mark", "status":true},
{"name":"Jane", "status":false}
];
var result = myArray.reduce(function(previousValue, currentObject){
return previousValue + (currentObject.status ? 1: 0);
}, 0);

Specifically:
var i = 0;
var count = 0;
while (i < array.length) {
if (array[i]['status'] == true) count += 1;
i += 1;
}
More generally, you can use some functional programming:
function count_matches(array, func) {
var i = 0;
var count = 0;
while (i < array.length) {
if (func(array[i])) count += 1;
i += 1;
}
return count;
}
function status_true(obj) {
return obj['status'] == true;
}
count_matches(array, status_true);
The above snippets do the same thing, but the latter is more flexible/potentially neater.

just loop over the array and count how many times the status property is true.
var count = 0;
for (var i = 0; i < yourArray.length; i++){
var current = yourArray[i];
if (current.status) count++
}

LinqJs would work (might be too much for the simple example posted in the question) -
http://linqjs.codeplex.com/
var jsonArray = [
{ "user": { "id": 100, "screen_name": "d_linq" }, "text": "to objects" },
{ "user": { "id": 130, "screen_name": "c_bill" }, "text": "g" },
{ "user": { "id": 155, "screen_name": "b_mskk" }, "text": "kabushiki kaisha" },
{ "user": { "id": 301, "screen_name": "a_xbox" }, "text": "halo reach" }]
// ["b_mskk:kabushiki kaisha", "c_bill:g", "d_linq:to objects"]
var queryResult = Enumerable.From(jsonArray)
.Where(function (x) { return x.user.id < 200 })
.OrderBy(function (x) { return x.user.screen_name })
.Select(function (x) { return x.user.screen_name + ':' + x.text })
.ToArray();
// shortcut! string lambda selector
var queryResult2 = Enumerable.From(jsonArray)
.Where("$.user.id < 200")
.OrderBy("$.user.screen_name")
.Select("$.user.screen_name + ':' + $.text")
.ToArray();

var obj = {
6:{"name":"Mary", "status":true},
2:{"name":"Mike", "status":true},
1:{"name":"John", "status":false},
4:{"name":"Mark", "status":true},
5:{"name":"Jane", "status":false}
};
var count = 0;
for (var prop in obj) {
if(obj[prop].status === true){
count += 1;
}
}
console.log("Output: "+count);
$("#debug").text("Output: "+count);
live demo http://jsbin.com/uwucid/2/edit

Related

Find combination of nested JSON keys

I am having some trouble solving this issue without using nested for-loops. I would like to do it using recursion.
Given the following object:
{
"Color": {
"Black": [],
"White": []
},
"Effect": {
"30W": [],
"40W": [],
"60W": []
}
}
The code should compute each combination of Color and Effect and add a number in the list such that the following is produced:
{
"Color": {
"Black": [
1,
2,
3
],
"White": [
4,
5,
6
]
},
"Effect": {
"30W": [
1,
4
],
"40W": [
2,
5
],
"60W": [
3,
6
]
}
}
My attempt is as follows:
const func = (object, entries) => {
for (let prop in object) {
let counter = 0;
const objLength = Object.keys(object[prop]).length;
for (let key in object[prop]) {
console.log(key + counter)
for (let i = 0; i < entries.length / objLength; i++) {
object[prop][key].push(entries[counter]);
counter++;
}
}
}
return object;
}
However, this does not return the desired output. I think it is because of the inner-most for loop condition.
The best way to handle this is to create your JavaScript object and convert it to a string.
// creating your object with attributes. Objects in objects or whatever you
// need
var obj = new Object();
obj.name = "Dale";
obj.age = 30;
obj.married = true;
dataToAdd.forEach(function(item, index) {
item.married = false;
})
// Then convert it to a string using the following code
var jsonString= JSON.stringify(obj);
console.log(jsonString);
I solved the question by considering the space of each attribute key. Then it is just a matter of finding the cartesian, and adding values accordingly:
const cartesian =(...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat())));
function diy(jsonObj, counter) {
let permObj = []
let keys = Object.keys(jsonObj)
keys.forEach(key => {
permObj.push(Object.keys(jsonObj[key]))
});
permObj = cartesian(...permObj)
for(let i = 0; i < keys.length; i++) {
for(let j = 0; j < permObj.length; j++) {
jsonObj[keys[i]][permObj[j][i]].push(j + counter);
}
}
return jsonObj;
}

How to return from a function multiple variables?

Edit:
Ultimately I want to print out something like this:
Scooter has 3 materials: "wood", "metal", "glass"; Baseball bat has two materials: "wood", metal" only using a for loop.
I'm using the following:
var data = [
{
"title": "scooter",
"materials": ["wood", "metal", "glass"]
},
{
"title": "baseball bat",
"materials": ["wood", "metal"]
},
{
"title": "coffee table",
"materials": ["wood"]
}];
I thought I could do a simple pass through with a for loop to get the title, count, and create an array with something like this:
function testing () {
let items = []; //contains the titles
let container = []; //contains the materials
let count = 0; //count of items
for (let i=0; i<data.length; i++){
if (data[i].materials.length > 2){
container.push(data[i].materials);
count = data[i].materials.length;
items.push(data[i].title)
}
}return container;
}
console.log(testing);
I don't know if the code is correct to get what I want out. Any thoughts?
Edit: So when I run this code I'm simply getting the list of the materials. When doing a return items and console.log(testing); I'm getting a list of the items.
It also looks like the count is just getting an array count of the titles not of the materials.
Further Edit with additional code:
function testing () {
let items = [];
let container = [];
let count = 0;
let result = '';
for (let i=0; i<data.length; i++){
if (data[i].materials.length > 2){
container.push(data[i].materials);
items.push(data[i].title)
for (let j=0; j<container.length; j++){
result= `${items[j]} has materials :${container[j]}`;
}
}
}return result;
}
console.log(testing());
With the above I can get the last title to show that meets the if statement with a list of items. I'm not however able to get a count of the materials. So for example it's simply showing:
Baseball bat has materials wood, metal
The question I'm facing then is how do I get the count and then display all objects that match. So it would be instead:
Scooter has 3 materials that are: wood, metal, glass
Baseball bat has 2 materials that are: wood, metal
If I understood your question correctly, you don't really need for loop. Arrays have map method, which can apply your function to each element of the array.
var data = [
{
"title": "scooter",
"materials": ["wood", "metal", "glass"]
},
{
"title": "baseball bat",
"materials": ["wood", "metal"]
},
{
"title": "coffee table",
"materials": ["wood"]
}];
var result = data.map(item => `${item.title} has ${item.materials.join(', ')}`).join('\n')
console.log(result);
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
See if the below code helps you in any way:
console.log(data.map(item => `${item.title} has ${item.materials.join(', ')}`).join('\n'))
All you need to do is map each of the items and concatenate the formatted strings.
You will need to know the count of the materials to display the quantity.
const capitalize = s => s.charAt(0).toUpperCase() + s.substr(1);
const quantity = x => 'one,two,three,four,...'.split(',')[x - 1];
var data = [{
"title": "scooter",
"materials": ["wood", "metal", "glass"]
}, {
"title": "baseball bat",
"materials": ["wood", "metal"]
}, {
"title": "coffee table",
"materials": ["wood"]
}
];
console.log(data.map(item => {
return [
capitalize(item.title),
' has ',
quantity(item.materials.length),
' material' + (item.materials.length > 1 ? 's' : '') + ': ',
item.materials.map(material => {
return '"' + material + '"';
}).join(', ')
].join('');
}).join('; '));
.as-console-wrapper { top: 0; max-height: 100% !important; }
first,
console.log(testing);
is just going to return the text of the function, you have to run the function, like this
console.log(testing());
Here's my function, written is ES6:
var data = [
{
"title": "scooter",
"materials": ["wood", "metal", "glass"]
},
{
"title": "baseball bat",
"materials": ["wood", "metal"]
},
{
"title": "coffee table",
"materials": ["wood"]
}];
function newTestingES6() {
let printStr = '';
for (let obj of data) {
printStr = `${obj.title} has ${obj.materials.length} materials: ${[...obj.materials]}`
console.log(printStr);
}
return 1;
}
function runTest() {
console.log(newTestingES6());
}
Try to write a function, that works for one object inside your array. If this works you can simple call this function inside a for-loop and push it to an array or use Array.map() which returns an array by default.
The Function
function createConstructionKit(object) {
var matarials = object.materials.join('", "') // this will give you for the first case -> wood", "metal", "glass
var title = object.title // no logic -> just did it to have lesser to write in the next line..
var capitilizedTitle = title.charAt(0).toUpperCase() + title.slice(1) // capitilize the first one and return all letters off the first index
return capitilizedTitle + ' has ' + object.materials.length + ' materials: "' + matarials + '"'
}
Solution with Map
var buildingInstructionsdata = data.map(function(object) {
return createConstructionKit(object)
})
Solution with a Loop
var buildingInstructionsdata = []
for (var i = 0; i < data.length; i++) {
buildingInstructionsdata.push(createConstructionKit(data[i]))
}
This was what I ended up getting to work:
function testing () {
for (let i = 0; i < data.length; i++) {
if (data[i].materials.length >2) {
console.log(`${data[i].title} has ${data[i].materials.length}
materials:`);
for (let j = 0; j < data[i].materials.length; j++) {
const material = data[i].materials[j];
console.log("- " + material);
}
}
}
}
console.log(testing());

Add or remove element(s) to array

I have an existing array of objects :
existingArray = [
{object1: 'object1'},
{object2: 'object2'}
{object3: 'object3'},
]
I receive a new one :
newArray = [
{object2: 'object2'},
{object3: 'object3'},
{object4: 'object4'}
]
I want only to modify the existing one to get the new one as the result (push+splice)
Here is what I have for now (is there a better way ?)
for (var i = 0; i < newArray.length; i++) {
// loop first to push new elements
var responseToTxt = JSON.stringify(newArray[i]);
var newStatement = false;
for(var j = 0; j < existingArray.length; j++){
var statementToTxt = JSON.stringify(existingArray[j]);
if(statementToTxt === responseToTxt && !newStatement){
newStatement = true;
}
}
if(!newStatement){
statements.push(response[i]);
}
}
var statementsToSplice = [];
for (var i = 0; i < existingArray.length; i++) {
// then loop a second time to split elements not anymore on the new array
var statementToTxt = JSON.stringify(existingArray[i]);
var elementPresent = false;
var element = false;
for(var j = 0; j < newArray.length; j++){
var responseToTxt = JSON.stringify(newArray[j]);
if(responseToTxt === statementToTxt && !elementPresent){
elementPresent = true;
} else {
element = i;
}
}
if(!elementPresent){
statementsToSplice.push(element);
}
}
Then I needed to split multiple times in the array :
existingArray = statementsToSplice.reduceRight(function (arr, it) {
arr.splice(it, 1);
return arr;
}, existingArray.sort(function (a, b) { return b - a }));
Here is the example :
https://jsfiddle.net/docmz22b/
So the final output should always be the new array, but only by push or splice the old one.
In this case, the final outpout will be
existingArray = [
{object2: 'object2'},
{object3: 'object3'}
{object4: 'object4'},
]
The new array could contains multiple new elements and/or deleted elements that is currently in the existingArray
Use shift() and push()
existingArray.shift(); //Removes the first element of the array
existingArray.push({'object4' : 'object4'});
Fiddle
I'm almost 100% sure that there is a better way to do it, but at least this works, feel free to comment any suggestions / optimizations.
existingArray = [
{object1: 'object1'},
{object2: 'object2'},
{object3: 'object3'}
];
newArray = [
{object2: 'object2'},
{object3: 'object3'},
{object4: 'object4'}
];
// Loop all the old values, if is not in the new array, remove it
existingArray.forEach(function(item) {
if(!inArray(item, newArray)) {
var idx = indexOfObjectInArray(item, existingArray);
existingArray.splice(idx, 1);
}
});
// Loop all the new values, if is not in the new array, push it
newArray.forEach(function(item) {
if (!inArray(item, existingArray)) {
existingArray.push(item);
}
});
// Auxiliar functions
function inArray(initialValue, array) {
testValue = JSON.stringify(initialValue);
return array.some(function(item) {
return testValue == JSON.stringify(item);
});
}
function indexOfObjectInArray(initialValue, array) {
var result = -1;
testValue = JSON.stringify(initialValue);
array.forEach(function(item, idx) {
if (testValue == JSON.stringify(item)) {
result = idx;
};
});
return result;
}
Maybe this helps. It features Array.prototype.forEach and Array.prototype.some.
Splice unwanted items
Look if object with same property exist
If yes, then assign new object
Else push the object
var existingArray = [
{ object1: 'object1' },
{ object2: 'object2' },
{ object3: 'object3' },
],
newArray = [
{ object2: 'object22' },
{ object3: 'object33' },
{ object4: 'object44' }
];
function update(base, change) {
var changeKeys = change.map(function (a) { return Object.keys(a)[0]; }),
i = 0;
while (i < base.length) {
if (!~changeKeys.indexOf(Object.keys(base[i])[0])) {
base.splice(i, 1);
continue;
}
i++;
}
change.forEach(function (a) {
var aKey = Object.keys(a)[0];
!base.some(function (b, i, bb) {
if (aKey === Object.keys(b)[0]) {
bb[i] = a; // if that does not work, use bb.splice(i, 1, a);
return true;
}
}) && base.push(a);
});
}
update(existingArray, newArray);
document.write('<pre>' + JSON.stringify(existingArray, 0, 4) + '</pre>');

Value inside loop doesn't change

Can someone please explain me how to change template.portNumber value ?
var template = {
portNumber: null,
stuff: ""
}
myfunc(template, 3);
function myfunc(template, count) {
var ports = {}
for (var i = 0; i < count; i++) {
var portNumber = i + 1;
ports[portNumber.toString()] = template;
ports[portNumber.toString()].portNumber = portNumber;
}
console.debug(JSON.stringify(ports, null, 4));
return ports;
}
Result:
"{
"1": {
"portNumber": 3,
"stuff": ""
},
"2": {
"portNumber": 3,
"stuff": ""
},
"3": {
"portNumber": 3,
"stuff": ""
}
}"
Expected:
"{
"1": {
"portNumber": 1,
"stuff": ""
},
"2": {
"portNumber": 2,
"stuff": ""
},
"3": {
"portNumber": 3,
"stuff": ""
}
}"
Sorry for the stupid question but i really stuck with it. Same code works well in python.
Thanks.
Your array ends up having three references to the same object so each time you mutate it the change is visible in all array elements.
Writing ports[0].port = "99" will in other words change also ports[1].port because ports[0] and ports[1] are the very same object.
You need to create a copy of the object instead...
The reason all array object are reference type so point to only one instance.
Try using constructor function like this.
var template = function(portno, stf){
this.portNumber = portno;
this.stuff = stf;
}
myfunc(template, 3);
function myfunc(template, count) {
var ports = {}
for (var i = 0; i < count; i++) {
var portNumber = i + 1;
ports[portNumber.toString()] = new template(portNumber , template.stuff);
}
console.debug(JSON.stringify(ports, null, 4));
return ports;
}
The template object is passed by reference, so all the items refer to the same object. It ends up looking a little like this:
template = {portNumber: 3, stuff: ""};
return {ports: {1:template, 2: template, 3: template}}
You need to clone the object, and then set it.
var template = {
portNumber: null,
stuff: ""
}
myfunc(template, 3);
function myfunc(template, count) {
var ports = {}
for (var i = 0; i < count; i++) {
var portNumber = i + 1;
ports[portNumber] = JSON.parse(JSON.stringify(template));
ports[portNumber].portNumber = portNumber;
}
console.debug(JSON.stringify(ports, null, 4));
return ports;
}
Also, you don't need to manually stringify a numerical key, it's done automatically.
This is a reference problem.
console.log(myFunc(template, 3));
function myFunc(template, count) {
var ports = {}
for (var i = 0; i < count; i++) {
var portNumber = i + 1;
ports[portNumber.toString()] = {portNumber:null, stuff:""};
ports[portNumber.toString()].portNumber = portNumber;
}
// console.debug(JSON.stringify(ports, null, 4));
return ports;
}
It's because template variable in your function holds the reference of the template object outside your function.
Try this:
function myFunc (template, count) {
var ports = {};
for(var i = 0; i < count; i++) {
var portNumber = i + 1;
var tmplt = JSON.parse(JSON.stringify(template));
tmplt.portNumber = portNumber;
ports[portNumber + ''] = tmplt;
}
return ports;
}

How to do lazy evaluation of an array being processed by a for loop

window.onload = function () {
x = '';
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, {a:x, b:''} ];
for (i = 0; i < myArray.length; i += 1) {
x = myArray[i].a + myArray[i].b;
}
alert(x); // alerts '';
}
Hi, the above is an example of what I'm trying to do. Basically, I would like for x to be evaluated after the 2nd array element computes it. I think this is called lazy evaluation, but not sure... I'm somewhat new.
How can I process my array in the loop and x be evaluated each time such that when I get to the third iteration, x = 'cd' and will alert as 'cd'?
I think I figured out the answer with your help and the other thread I mentioned in the comment. Just need to wrap x in a function and then define a get function to apply to all elements:
window.onload = function () {
function get(e) {return (typeof e === 'function') ? e () : e; }
var x = '';
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, {a:function() {return x; }, b:''} ];
for (i = 0; i < myArray.length; i += 1) {
x = get(myArray[i].a) + get(myArray[i].b);
}
alert(x); // alerts 'cd';
}
x can be anything then. For example (x + 'xyz') will alert 'cdxyz'. So this way I can have any variable that I want evaluated later (when needed) be evaluated correctly (based on state at that point).
That's what I needed. :)
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function getter(list, num) {
var i, agg = { a: "", b: "" };
for (i = 0; i <= num; i += 1) {
agg.a += list[i].a;
}
return agg;
}
console.log(getter(elements, 0).a); // "a"
console.log(getter(elements, 1).a); // "ac"
console.log(getter(elements, 2).a); // "ace"
You can use a closure so you can't access the values, like:
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function make_getter(list) {
return {
get: function (num) {
var i, agg = { a: "", b: "" };
for (i = 0; i <= num; i += 1) {
agg.a += list[i].a;
}
return agg;
}
};
}
var getter = make_getter(elements);
console.log(getter.get(0).a); // "a"
console.log(getter.get(1).a); // "ac"
console.log(getter.get(2).a); // "ace"
You can make different implementations of the aggregation function.
With recursion:
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function getter(list, num) {
var i, agg = list[num];
if (num > 0) {
agg.a = getter(list, num-1).a + agg.a;
}
return agg;
}
console.log(getter(elements, 0).a); // "a"
console.log(getter(elements, 1).a); // "ac"
console.log(getter(elements, 2).a); // "aace" <-- note, elements are actually modified!
console.log(getter(elements, 2).a); // "aaacaace" <-- note, elements are actually modified!
old answer
Since x is not an object it's value will be copied, rather than passed as a reference.
If you change your code to:
var element = { a: '', b:'' };
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, element ];
for (i = 0; i < myArray.length; i += 1) {
element.a = myArray[i].a + myArray[i].b;
}
alert(el.a); // alerts 'cd';
You will get "cd".
This is not called lazy evaluation by the way. It's just an aggregate or something.

Categories

Resources