i need to count the number of occurrences of values in jsonArray items in javascript.
consider this as my jsonArray:
{"name":"jack","age":23},
{"name":"john","age":20},
{"name":"alison","age":23},
{"name":"steve","age":25},
.
.
.
now for example i need to know how many times each age is repeated in this array, i want to count each value for each property, i mean i need a result like this :
name : {"jack" : 2,"james" : 10,"john" : 1,....}
age : {"23":10,"20":15,"25":2,....}
what is the simplest way to achieve this?
EDIT :
my array is very big and i don't want to call count function for each value. i want a code to count each value repeat times.
you can have a look at Array.prototype.reduce
function mapToProp(data, prop) {
return data
.reduce((res, item) => Object
.assign(res, {
[item[prop]]: 1 + (res[item[prop]] || 0)
}), Object.create(null))
;
}
const data = [
{"name": "jack", "age": 23},
{"name": "john", "age": 20},
{"name": "alison", "age": 23},
{"name": "steve", "age": 25}
];
console.log('mapAndCountByAge', mapToProp(data, 'age'))
You could use an object, where the keys and the values are used as property for the count.
var array = [{ name: "jack", age:23 }, { 'name': "john", age: 20 }, { name: "alison", age: 23 }, { name: "steve", age: 25 }],
result = {};
array.forEach(function (o) {
Object.keys(o).forEach(function (k) {
result[k] = result[k] || {};
result[k][o[k]] = (result[k][o[k]] || 0) + 1;
});
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Not as elegant as Nina's and Hitmands' answers, but this is easier to understand for beginners (in case any beginner is reading this).
var jsonArray = [
{"name": "John", "age": 20},
{"name": "John", "age": 21},
{"name": "Doe", "age": 21},
{"name": "Doe", "age": 23},
{"name": "Doe", "age": 20},
];
var names = [],
ages = [];
// separate name data and age data into respective array
// only need to access the original data once
jsonArray.forEach(function(val){
names.push(val['name']);
ages.push(val['age']);
});
function aggregate(array){
var obj = {};
array.forEach(function(val){
if (!obj[val])
// create new property if property is not found
obj[val] = 1;
else
// increment matched property by 1
obj[val]++;
});
return JSON.stringify(obj);
}
console.log("Names: " + aggregate(names));
console.log("Ages: " + aggregate(ages));
In my opinion, Nina has the best answer here.
EDIT
In fact, when presented with large dataset (as OP stated that "the array is very big"), this method is about 45% faster than Nina's answer and about 81% faster than Hitmand's answer according to jsben.ch. I don't know why though.
Related
I have an array of objects and want to filter it based on values of another string array and remove the objects that doesn't contain any of the strings.
I tried using split for the string array and search for each term in a forEach but it didn't work then tried the following which works as a filter but it filters for exact match not partial.
var employees = [
{"name": "zxc asd", "age":"30"},
{"name": "asd", "age":"24"},
{"name": "qwe", "age":"44"},
{"name": "zxc", "age":"28"},
];
var keepNames = ["asd", "qwe"];
keepNames = keepNames.map(name => {
return name.toLowerCase();
});
var filteredEmployees = employees.filter(emp => {
return keepNames.indexOf(emp.name.toLowerCase()) !== -1;
});
console.log( filteredEmployees );
Expected Output[
{"name": "zxc asd", "age":"30"},
{"name": "asd", "age":"24"},
{"name": "qwe", "age":"44"}];
Actual Output [
{"name": "asd", "age":"24"},
{"name": "qwe", "age":"44"}]
I'd really appreciate any help.
Thanks in advance
You need to iterate the array with names keep as well and check the value.
var employees = [{ name: "zxc asd", age: "30" }, { name: "asd", age: "24" }, { name: "qwe", age: "44" }, { name: "zxc", age: "28" }],
keep = ["asd", "qwe"],
filtered = employees.filter(({ name }) =>
keep.some(n => name.toLowerCase().includes(n))
);
console.log(filtered);
.as-console-wrapper { max-height: 100% !important; top: 0; }
indexOf uses strict equality so it won't match partially.
You can use some and includes
var employees = [ {"name": "zxc asd", "age":"30"},{"name": "asd", "age":"24"},{"name": "qwe", "age":"44"},{"name": "zxc", "age":"28"},];
var filterBy = ["asd", "qwe"];
var filteredEmployees = employees.filter(emp => {
return filterBy.some(v => emp.name.toLowerCase().includes(v.toLowerCase()))
});
console.log(filteredEmployees);
I have a problem with filtering an array with nested objects.
[{
"firstName": "Kevin",
"lastName": "Smith",
"expenses": {
"drink1": 25,
"drink2": 20
}
},
{
"firstName": "John",
"lastName": "Rambo",
"expenses": {
"coffe": 10,
"cake": 20
}
}
]
I want to get the objects where the sum of all expenses is > 35. How to get inside expenses? Or maybe filter is not a proper way here.
Just filter it, with a condition using reduce to sum the expenses! Pretty straight forward :)
const input = [{
"firstName": "Kevin",
"lastName": "Smith",
"expenses": {
"drink1": 26,
"drink2": 20
}
},
{
"firstName": "John",
"lastName": "Rambo",
"expenses": {
"coffe": 10,
"cake": 20
}
}
];
const output = input.filter(user => Object.values(user.expenses).reduce((acc, expense) => acc + expense) > 45);
console.log(output);
I'm assuming that you need to keep you array of users the way it is, but with the expenses filtered.
(I assumed wrong as pointed in the comment, keeping this answer just in case someone sees any value on it)
Probably this can be optmized or simplified, but here it goes:
arr.reduce((acc, user) => [...acc,
Object.keys(user).reduce((userResult, key) => {
if (key === 'expenses') {
return {
...userResult,
expenses: Object.entries(elem.expenses)
.filter(([product, value]) => value > 35)
// now "reversing" the object.entries
.reduce((acc, [product, value]) => ({ [product]: value }), {})
}
}
return {
...userResult,
[key]: elem[key]
}
}, user) // starts with the user
], []) //starts with empty array
You can try something like this
var data = [{
"firstName": "Kevin",
"lastName": "Smith",
"expenses": {
"drink1": 25,
"drink2": 20
}
},
{
"firstName": "John",
"lastName": "Rambo",
"expenses": {
"coffe": 10,
"cake": 20
}
}
]
var filtered = data.filter(c => Object.keys(c.expenses)
.reduce(function(p, e) {
return p + c.expenses[e]
}, 0) >= 35
);
console.log(filtered);
You can access object at index i inside array arr with expression arr[i]
What you need to do is to loop over your array. Inside your loop you access each object with expression i have mentioned: arr[i] and then on this object you can access expenses following way arr[i].expenses after this - if i am understanding correctly you sum contents of your arr[i].expenses object and select those objects which satisfy your condition.
Please, See code below:
var expensesAbove35Arr = [];
var yourArray = [
{
"firstName": "Kevin",
"lastName": "Smith",
"expenses": {
"drink1": 26,
"drink2": 20
}
},
{
"firstName": "John",
"lastName": "Rambo",
"expenses": {
"coffe": 10,
"cake": 20
}
}
];
for(var i=0; i<yourArray.length; i++){
if(yourArray[i].expenses.coffe + yourArray[i].expenses.cake > 35 ){
expensesAbove35Arr.push(yourArray[i]);
}
}
You have got your result inside array expensesAbove35Arr
A possible solution might be:
arr.filter(function(v){
for(expense in v.expenses){
if(v.expenses[expense] > 10){
return true;
}else{
return false;
}
}
})
Suppose I am having two arrays namely namesArray and names as below
var namesArray = [{"name":"Charlie","age":3},{"name":"Dog","age":1},{"name":"Baker","age":7},{"name":"Abel","age":9}];
var names = ['Baker', 'Dog', 'Abel', 'Charlie'];
Can I achieve the following in UnderscoreJS so that sort the array named namesArray in a order so that all the name elements of namesArray will be in the same order of names .
In plain Javascript, you could use Array#sort with an object as reference for the sort order.
var namesArray = [{ "name": "Charlie", "age": 3 }, { "name": "Dog", "age": 1 }, { "name": "Baker", "age": 7 }, { "name": "Abel", "age": 9 }],
names = ['Baker', 'Dog', 'Abel', 'Charlie'],
hash = Object.create(null);
names.forEach(function (a, i) {
hash[a] = i + 1;
});
namesArray.sort(function (a, b) {
return (hash[a.name] || 0) - (hash[b.name] || 0);
});
console.log(namesArray);
Look up the index of each name in sort(). Not as performant as creating hash of indexes but chances are it's not that critical either
namesArray.sort(function(a,b){
return names.indexOf(a.name) - names.indexOf(b.name);
});
There is not lot to do with underscrore but you can do it like this:
I think using hash like what Nina does would be better than indexOf with respect to performance.
var namesArray = [{ "name": "Charlie", "age": 3 }, { "name": "Dog", "age": 1 }, { "name": "Baker", "age": 7 }, { "name": "Abel", "age": 9 }];
var names = ['Baker', 'Dog', 'Abel', 'Charlie'];
var sorted = _.sortBy(namesArray, o => names.indexOf(o.name));
console.log(sorted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
Iam trying to iterate the below JSON with linq.js , Here my scenario is I am having an array which contains 3,5,6.Based on this array values i need firstName from below JSON. How can i achieve it . The JSON is just given for an example in real time am handling with 50000 records, what is the best way to achieve this?
[
{
"id": "1",
"firstName": "John",
"lastName": "Doe"
},
{
"id": "2",
"firstName": "Anna",
"lastName": "Smith"
},
{
"id": "3",
"firstName": "Peter",
"lastName": "Jones"
},
{
"id": "4",
"firstName": "John",
"lastName": "Doe"
},
{
"id": "5",
"firstName": "Anna",
"lastName": "Smith"
},
{
"id": "6",
"firstName": "Peter",
"lastName": "Jones"
},
{
"id": "7",
"firstName": "John",
"lastName": "Doe"
},
{
"id": "8",
"firstName": "Anna",
"lastName": "Smith"
},
{
"id": "9",
"firstName": "Peter",
"lastName": "Jones"
}
]
Basically what you're trying to do is a join. A naive approach would simply just do a join.
var ids = [ 3, 5, 6 ];
var query = Enumerable.From(data)
.Join(ids, "Number($.id)", "$", "$")
.ToArray();
However this wouldn't work out well when there are many objects to work with since you're performing a linear search for the associated items. You can improve things by creating a lookup of the objects by id. You'll just have to pay the up front cost of creating the lookup.
var lookup = Enumerable.From(data)
.ToObject("Number($.id)");
var ids = [ 3, 5, 6 ];
var query = Enumerable.From(ids)
.Select(function (id) {
return lookup[id];
})
.ToArray();
If you are aware of that big amount of data would hang other process, make the user feel laggy, you can try this:
var process = function(obj, targets, callback, context) {
// Init context
var results = [];
var length = obj.length;
var procLimit = 500; // Whatever the amount you want to process at a time, higher will have higher change to cause lag on browser.
context = context ? context : null;
var current = 0;
// Loop function
var looper = function() {
if (current >= length) {
callback.call(context, result);
return;
}
var end = Math.min(length, current + procLimit);
var id, findIndex;
// Only process a fixed amount of item in a time.
for (; current < end ; ++current) {
id = parseInt(obj[current].id, 10);
findIndex = targets.indexOf(id);
// Find the matched key, and put to result.
if (findIndex >= 0) {
results.push(obj[current]);
// or you just need fname, use
// results.push(obj[current].firstName);
// remove founded from targets
targets.splice(findIndex, 1);
}
}
current += procLimit;
// Non-blocking
setTimeout(looper, 10);
};
// Non-blocking
setTimeout(looper, 10);
};
// Usage
process(YOUR_OBJ, [1, 3, 5, 6], function(processedObj){
// do somthing with the processed Object
}, THE_CONTEXT);
This function will tries to run as in background, and when it complete scan through the json array, it'll call your handler with the founded items in an array as param.
If block-effect is not in concern, then simply use the filter:
var targets = [1, 3, 5, 6];
var filterResult = YOUR_JSON.filter(function(item) {
var id = parseInt(item.id, 10);
return (targets.indexOf(id) >= 0);
}).map(function(item) { return item.firstName; });
I have a two dimensional array, datas, that I want to convert to an array of objects.
The keys are in datas[0], I want to extract them, name, child, and size. and then append each attribute to it to get a master object. For some reason it overrides and is only showing one object when I try this?
var test = new Object();
for (i = 0; i < datas.length; i++){
var obj = new Object();
obj.name = datas[i][0];
obj.parent = datas[i][1];
obj.size = datas[i][2];
test.update(obj);
}
I would like the final result to be:
[
{"name": "Billy", "parent":"Ann", "size": "1"},
{"name": "Ben", "parent": "John", "size": "1"},
etc...
]
The datas array looks like:
[["Name", "Parent", "Size"], ["Billy", "Ann", "1"], ["Ben", "John", "1"] ... ]
You can't make an object without properties, so your desired result can't be achieved.
Assuming you want:
[
{"name": "Billy", "parent": "Ann", "size": "1"},
{"name": "Ben", "parent": "John", "size": "1"},
etc...
]
Try:
var test = [];
for(i = 0; i < datas.length; i++){
test.push({
name: datas[i][0],
parent: datas[i][1],
size: datas[i][2]
});
}
// do something with test
{
{"name": "Billy", "parent":"Ann", "size"="1"},
{"name": "Ben", "parent": "John", "size" = "1"}
}
is not correct json. curly braces mean - object, object must be presented in key:value form. There are two possible correct json of this kind:
array of objects
[
{"name": "Billy", "parent":"Ann", "size"="1"},
{"name": "Ben", "parent": "John", "size" = "1"}
]
deep structure
{
"Billy" {"parent":"Ann", "size"="1"},
"Ben" {"parent": "John", "size" = "1"}
}
to generate first variant
var res = []
for(i = 0; i<datas.length; i++){
var obj = new Object();
obj.name = datas[i][0];
obj.parent = datas[i][1];
obj.size = datas[i][2];
res.push(obj);
}
JSON.stringify(res);
to generate second variant
var res = new Object();
for(i = 0; i<datas.length; i++){
var obj = new Object();
obj.parent = datas[i][1];
obj.size = datas[i][2];
res.[datas[i][0]] = obj;
}
JSON.stringify(res);