I made a small plnkr here to show what I am trying to achieve. I have a big dataset, where I like to sum the individual type to get a total.
I could think of iterating and adding the results to an object hash, but wonder more elegant way to solve it with underscore. I am using underscore.js, but never tried map reduce or other functional paradigm. Please update the plnkr to learn how to do this.
http://plnkr.co/edit/B5HGxhwvWsfvOR97z7TL?p=preview
var data = [ {'type': "A", 'val':2},
{'type': "B", 'val':3},
{'type': "A", 'val':1},
{'type': "C", 'val':5} ];
_.each(data, function (elm, index) {
console.log(elm);
});
/*
Desired output
out = [ {'type': "A", 'total':3},
{'type': "B", 'total':3},
{'type': "C", 'total':5} ];
*/
var data = [ { type: "A", val: 2 },
{ type: "B", val: 3 },
{ type: "A", val: 1 },
{ type: "C", val: 5 } ];
var groups = _(data).groupBy('type');
var out = _(groups).map(function(g, key) {
return { type: key,
val: _(g).reduce(function(m,x) { return m + x.val; }, 0) };
});
DEMO
Pretty much the same answer as #GregL, just with a bit more underscore:
summed_by_type = _(data).reduce(function(mem, d) {
mem[d.type] = (mem[d.type] || 0) + d.val
return mem
}, {})
pairs = _(summed_by_type).map(function(v,k) { return {type: k, total: v} })
The following will work, but I assume it is similar to what you had in mind. The advantage is that by using an object hash to store the totals, you are indexing on the type which means you don't have to iterate through the hash each time trying to find the object with the right type. Then you iterate through it once at the end to build up the final output array.
Plunkr is here.
Code is as follows:
var data = [ {'type': "A", 'val':2},
{'type': "B", 'val':3},
{'type': "A", 'val':1},
{'type': "C", 'val':5} ];
var totalPerType = {};
for (var i = 0, len = data.length; i < len; ++i) {
totalPerType[data[i].type] = totalPerType[data[i].type] || 0;
totalPerType[data[i].type] += data[i].val;
}
var out = _.map(totalPerType, function(sum, type) {
return { 'type': type, 'total': sum };
});
console.log('out = ', out);
EDIT: I have created a new plunkr that generates how fast this is even for a 1 million item array (with 6 possible types) here. As you can see from the console output, at least in Chrome Canary, it runs in about 1/3 second.
I have also done a jsPerf test for how much faster it is to use the intermediate hash, and it works out about 50% faster.
Related
I have been looking a simple way to copy/insert/move properties in an object within an array to another object. I came up with a basic logic which does the job perfectly but am not satisfied with this. There has to be a better way, any help here?
var first = [
{
"AGREE_EFF_DATE__0": "02-Aug-2018",
"AGREE_TERM_DATE__0": "30-Apr-2021",
"AGREE_IND__0": "P1",
"P_DBAR_IND__0": "N",
"AGREE_EFF_DATE__1": "01-May-2021",
"AGREE_TERM_DATE__1": null,
"AGREE_IND__1": "NP",
"P_DBAR_IND__1": "N",
"PROVIDER_SPECIALITY__0": "PSYCHOLOGY, CLINICAL",
"PROVIDER_SPECIALITY_CODE__0": "CK"
}
];
var second = [
{
"STATUS": "ACTIVE",
"MEDICARE_NUMBER" : 12345
}
];
for(let i = 0; i < second.length; i++) {
var first_keys = Object.keys(first[i]);
var first_values = Object.values(first[i]);
for(let j = 0; j < first_keys.length; j++) {
second[i][first_keys[j]] = first_values[j];
}
}
console.log(second);
//Output-
[
{
STATUS: 'ACTIVE',
MEDICARE_NUMBER: 12345,
AGREE_EFF_DATE__0: '02-Aug-2018',
AGREE_TERM_DATE__0: '30-Apr-2021',
AGREE_IND__0: 'P1',
P_DBAR_IND__0: 'N',
AGREE_EFF_DATE__1: '01-May-2021',
AGREE_TERM_DATE__1: null,
AGREE_IND__1: 'NP',
P_DBAR_IND__1: 'N',
PROVIDER_SPECIALITY__0: 'PSYCHOLOGY, CLINICAL',
PROVIDER_SPECIALITY_CODE__0: 'CK'
}
]
When possible, you should prefer iteration to manually indexed loops. This means arr.map() or arr.forEach() or arr.reduce(), to name a few.
Also, You can use an object spread to easily merge objects together.
Putting those together, you can reduce this logic to:
const result = first.map((firstObj, i) => ({ ...firstObj, ...second[i] }))
Here we map() over all members of first, which returns a new array where each member is the result of the function. This function takes the array member as the first argument, and the index of that member as the second argument. Then we can use that index to find the corresponding item in the second array.
Then you just spread both objects into a new object to assemble the final result.
var first = [
{ a: 1, b: 2 },
{ a: 4, b: 5 },
];
var second = [
{ c: 3 },
{ c: 6 },
];
const result = first.map((firstObj, i) => ({ ...firstObj, ...second[i] }))
console.log(result)
Which is all perfectly valid typescript as well.
NOTE: there is one difference between my code any yours. Your code modifies the objects in second. My code returns new objects and does not change the contents of second at all.
This is usually the better choice, but it depends on how you use this value and how data is expected to flow around your program.
You need to be careful with iterating, because you can have different count of elements in first and second arrays. So the possible solution will be like this:
const first = [
{
"AGREE_EFF_DATE__0": "02-Aug-2018",
"AGREE_TERM_DATE__0": "30-Apr-2021",
"AGREE_IND__0": "P1",
"P_DBAR_IND__0": "N",
"AGREE_EFF_DATE__1": "01-May-2021",
"AGREE_TERM_DATE__1": null,
"AGREE_IND__1": "NP",
"P_DBAR_IND__1": "N",
"PROVIDER_SPECIALITY__0": "PSYCHOLOGY, CLINICAL",
"PROVIDER_SPECIALITY_CODE__0": "CK"
}
];
const second = [
{
"STATUS": "ACTIVE",
"MEDICARE_NUMBER": 12345
}
];
console.log(mergeAll(first, second));
function mergeAll(firstArray, secondArray) {
const result = [];
const minLength = firstArray.length < secondArray.length ? firstArray.length : secondArray.length;
for (let i = 0; i < minLength; i++) {
result.push({...firstArray[i], ...secondArray[i]});
}
return result;
}
I have an array of objects and I'm wondering the best way to search it. Given the below example how can I search for name = "Joe" and age < 30? Is there anything jQuery can help with or do I have to brute force this search myself?
var names = new Array();
var object = { name : "Joe", age:20, email: "joe#hotmail.com"};
names.push(object);
object = { name : "Mike", age:50, email: "mike#hotmail.com"};
names.push(object);
object = { name : "Joe", age:45, email: "mike#hotmail.com"};
names.push(object);
A modern solution with Array.prototype.filter():
const found_names = names.filter(v => v.name === "Joe" && v.age < 30);
Or if you still use jQuery, you may use jQuery.grep():
var found_names = $.grep(names, function(v) {
return v.name === "Joe" && v.age < 30;
});
You can do this very easily with the [].filter method:
var filterednames = names.filter(function(obj) {
return (obj.name === "Joe") && (obj.age < 30);
});
You can learn more about it on this MDN page.
You could utilize jQuery.filter() function to return elements from a subset of the matching elements.
var names = [
{ name : "Joe", age:20, email: "joe#hotmail.com"},
{ name : "Mike", age:50, email: "mike#hotmail.com"},
{ name : "Joe", age:45, email: "mike#hotmail.com"}
];
var filteredNames = $(names).filter(function( idx ) {
return names[idx].name === "Joe" && names[idx].age < 30;
});
$(filteredNames).each(function(){
$('#output').append(this.name);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"/>
var nameList = [
{name:'x', age:20, email:'x#email.com'},
{name:'y', age:60, email:'y#email.com'},
{name:'Joe', age:22, email:'joe#email.com'},
{name:'Abc', age:40, email:'abc#email.com'}
];
var filteredValue = nameList.filter(function (item) {
return item.name == "Joe" && item.age < 30;
});
//To See Output Result as Array
console.log(JSON.stringify(filteredValue));
You can simply use javascript :)
For those who want to filter from an array of objects using any key:
function filterItems(items, searchVal) {
return items.filter((item) => Object.values(item).includes(searchVal));
}
let data = [
{ "name": "apple", "type": "fruit", "id": 123234 },
{ "name": "cat", "type": "animal", "id": 98989 },
{ "name": "something", "type": "other", "id": 656565 }]
console.log("Filtered by name: ", filterItems(data, "apple"));
console.log("Filtered by type: ", filterItems(data, "animal"));
console.log("Filtered by id: ", filterItems(data, 656565));
filter from an array of the JSON objects:**
var names = [{
name: "Joe",
age: 20,
email: "joe#hotmail.com"
},
{
name: "Mike",
age: 50,
email: "mike#hotmail.com"
},
{
name: "Joe",
age: 45,
email: "mike#hotmail.com"
}
];
const res = _.filter(names, (name) => {
return name.name == "Joe" && name.age < 30;
});
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
So quick question. What if you have two arrays of objects and you would like to 'align' these object arrays so that you can make sure each array's objects are in the order as the other array's? What if you don't know what keys and values any of the objects inside of the arrays contains... Much less what order they're even in?
So you need a 'WildCard Expression' for your [].filter, [].map, etc. How do you get a wild card expression?
var jux = (function(){
'use strict';
function wildExp(obj){
var keysCrude = Object.keys(obj),
keysA = ('a["' + keysCrude.join('"], a["') + '"]').split(', '),
keysB = ('b["' + keysCrude.join('"], b["') + '"]').split(', '),
keys = [].concat(keysA, keysB)
.sort(function(a, b){ return a.substring(1, a.length) > b.substring(1, b.length); });
var exp = keys.join('').split(']b').join('] > b').split(']a').join('] || a');
return exp;
}
return {
sort: wildExp
};
})();
var sortKeys = {
k: 'v',
key: 'val',
n: 'p',
name: 'param'
};
var objArray = [
{
k: 'z',
key: 'g',
n: 'a',
name: 'b'
},
{
k: 'y',
key: 'h',
n: 'b',
name: 't'
},
{
k: 'x',
key: 'o',
n: 'a',
name: 'c'
}
];
var exp = jux.sort(sortKeys);
console.log('#juxSort Expression:', exp);
console.log('#juxSort:', objArray.sort(function(a, b){
return eval(exp);
}));
You can also use this function over an iteration for each object to create a better collective expression for all of the keys in each of your objects, and then filter your array that way.
This is a small snippet from the API Juxtapose which I have almost complete, which does this, object equality with exemptions, object unities, and array condensation. If these are things you need or want for your project please comment and I'll make the lib accessible sooner than later.
Hope this helps! Happy coding :)
The most straightforward and readable approach will be the usage of native javascript filter method.
Native javaScript filter takes a declarative approach in filtering array elements. Since it is a method defined on Array.prototype, it iterates on a provided array and invokes a callback on it. This callback, which acts as our filtering function, takes three parameters:
element — the current item in the array being iterated over
index — the index or location of the current element in the array that is being iterated over
array — the original array that the filter method was applied on
Let’s use this filter method in an example. Note that the filter can be applied on any sort of array. In this example, we are going to filter an array of objects based on an object property.
An example of filtering an array of objects based on object properties could look something like this:
// Please do not hate me for bashing on pizza and burgers.
// and FYI, I totally made up the healthMetric param :)
let foods = [
{ type: "pizza", healthMetric: 25 },
{ type: "burger", healthMetric: 10 },
{ type: "salad", healthMetric: 60 },
{ type: "apple", healthMetric: 82 }
];
let isHealthy = food => food.healthMetric >= 50;
const result = foods.filter(isHealthy);
console.log(result.map(food => food.type));
// Result: ['salad', 'apple']
To learn more about filtering arrays in functions and yo build your own filtering, check out this article:
https://medium.com/better-programming/build-your-own-filter-e88ba0dcbfae
Essentially, I want to implement the following:
var categories = [];
var products = // some array of product objects
products.map(function(value) {
if(categories.indexOf(value.Category === -1)) categories.push(value.Category);
});
As result, categories array contains unique list of product categories.
I feel that there should be a better way to do it, but nothing comes to mind.
If there isn't then probably there is no point to use map() in the first place. I could do as simple as
var categories = [];
var products = // some array of product objects
for (var i = 0; i < products.length; i++) {
if(categories.indexOf(products[i].Category === -1)) categories.push(products[i].Category);
}
UPDATE for those who insist it's a duplicate of "how to make an array unique" question. I saw that post, and for my situation I don't think it applies. I don't have an array of values that I need to make unique. I have an array of objects and I need to build an array of unique values. The difference might be subtle - but to get to the use case of that topic I would build a non-unique array and then make it unique. Seems even worse than my original solution
you can use reduce instead of map
var products = [{Category:'vegetable', price: 1}, {Category:'fruits', price: 2}];
var categories = products.reduce(function(sum, product) {
if(sum.indexOf(product.Category) === -1){
sum.push(product.Category);
}
return sum;
}, []);
Update: A solution with Array.prototype.reduce()
var products = [{ Name: 'milk', price: 2.50, Category: 'groceries' }, { Name: 'shirt', price: 10, Category: 'clothing' }, { Name: 'apples', price: 5, Category: 'groceries' }],
categories = products.reduce(function (r, a) {
if (!~r.indexOf(a.Category)) {
r.push(a.Category);
}
return r;
}, []);
document.write('<pre>' + JSON.stringify(categories, 0, 4) + '</pre>');
map all the values of the object categories out first, then use filter to dispose of the duplicates.
var products = [
{ category: 'A' },
{ category: 'B' },
{ category: 'A' },
{ category: 'D' }
];
var categories = products.map(function (e) {
return e.category;
}).filter(function (e, i, a) {
return a.indexOf(e) === i;
}); // [ "A", "B", "D" ]
DEMO
Follow the Below SO Answer:
How to get distinct values from an array of objects in JavaScript?
var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
if( flags[array[i].age]) continue;
flags[array[i].age] = true;
output.push(array[i].age);
}
I have this Javascript json array object;
var dataJson=[ [{v:1},{v:90}],[{"v":2},{"v":"33.7000"}] ];
I want to append this array object dataJson to another object such that it looks like this;
var chartObject = {"cols": [
{id: "t", label: "t_label", type: "number"},
{id: "s", label: "s_label", type: "number"}
], "rows": [
{c:
[{v:1},{v:90}] //dataJson[0]
},
{c:
[{"v":2},{"v":"33.7000"}] ////dataJson[1]
}
]};
How do I use a for loop to insert dataJson elements into chartObject? I am sorry I am quite new to javascript and can't even produce some starting code. Thank you very much for any help.
Try this:
...
], "rows": dataJson.map(function(row) {return {c:row};})
};
Javascript objects are pretty amazing things. Just define a new field in chartObject as an array, and then push whatever json data you want into it. It looks you want rows to be an array of objects which have an identifier for each json object, but unless you explicitly want to name each dataJson with a string, then just use an indexed array:
chartObject["rows"] = [];
for(var i = 0; i < dataJson.length; i++) {
chartObject["rows"].push(dataJson[0]);
}
Now you can access each piece of data with:
chartObject["rows"][index]
And each field in the data with:
chartObject["rows"][index]["v"]
Using the simple and clean way:
var chartObject = {"cols": [
{id: "t", label: "t_label", type: "number"},
{id: "s", label: "s_label", type: "number"}
]};
var dataJson=[ [{v:1},{v:90}],[{"v":2},{"v":"33.7000"}] ];
chartObject["rows"] = []; // start with empty array
// iterate over first dataJson array
for(var i = 0, len = dataJson[0].length; i < len; i++){
// push in array `c` mapped to the i-th element of dataJson[0]
chartObject["rows"].push({c : dataJson[0][i]["v"]});
}
console.log(chartObject);
DEMO
Ignore those [object Object] in DEMO
Sample Output:
{
cols: [{
id: "t",
label: "t_label",
type: "number"
}, {
id: "s",
label: "s_label",
type: "number"
}],
rows: [{
c: 1
}, {
c: 90
}]
}
Given an array of objects:
{
key: "a",
value: 42
},
{
key: "d",
value: 28
},
{
key: "c",
value: 92
},
{
key: "b",
value: 87
}
and an array of keys:
["c", "a", "b", "d"]
Is there a ECMAScript function or a 3rd-party JavaScript library that lets you sort - in one line/function call - the first array of objects, to match the order of the keys specified in the second array, such that the result is:
{
key: "c",
value: 92
},
{
key: "a",
value: 42
},
{
key: "b",
value: 87
},
{
key: "d",
value: 28
}
Other questions that provide a function or algorithm:
Javascript - sort array based on another array - Stack Overflow
javascript - How do I sort an array of objects based on the ordering of another array? - Stack Overflow
Similar/related questions:
Sorting an Array of Objects in PHP In a Specific Order
php - Sort array of objects
Just use indexOf to convert the key to the correct order:
var order = ["c", "a", "b", "d"];
_.sortBy(arr, function(obj){
return _.indexOf(order, obj.key);
});
Fiddle
If there are a lot of keys, then it would be advantageous to make a hash-map out of the array, like:
var order = ["c", "a", "b", "d"];
var orderMap = {};
_.each(order, function(i) { orderMap[i] = _.indexOf(order, i); });
This makes the key-sorting lookup constant time rather than O(n). (Fiddle)
Great answers provided so far. Thought that the following may also be an alternative solution in plain JS:
var arr = arr.sort(function(a,b) {
return order.indexOf( a.key ) - order.indexOf( b.key );
//for the sake of recent versions of Google Chrome use:
//return a.key.charCodeAt(0) > b.key.charCodeAt(0); or return a.key.charCodeAt(0) - b.key.charCodeAt(0);
});
var arr = [
{
key: "a",
value: 42
},
{
key: "d",
value: 28
},
{
key: "c",
value: 92
},
{
key: "b",
value: 87
}
];
var order = ["c", "a", "b", "d"];
console.log( 'Original: ', JSON.stringify( arr ) );
var arr = arr.sort(function(a,b) {
return order.indexOf( a.key ) - order.indexOf( b.key );
});
console.log( 'Ordered: ', JSON.stringify( arr ) );
const obj = [
{
key: "a",
value: 42
},
{
key: "d",
value: 28
},
{
key: "c",
value: 92
},
{
key: "b",
value: 87
}
]
const sortList = ["c", "a", "b", "d"];
const sortedObj = obj.sort((a, b) => {
return (
sortList.indexOf(a.key) - sortList.indexOf(b.key)
);
});
console.log(sortedObj );
I can't claim that this is the most efficient way, but you can use the key for each object as a key for properties in another object. Then simply access them by these keys.
for (x = 0; x < objn.length; x++) {
newobj[objn[x].key] = objn[x];
}
objn = [];
for (x = 0; x < keys.length; x++) {
objn.push(newobj[keys[x]]);
}
console.log(objn);
http://jsfiddle.net/WdehF/
// create hash map el.key -> index, to help us with direct access, avoid searching
const hashMap = arr.reduce((acc, el, index) => { acc[el.id] = el; return acc }, {})
// finally, map the ids to the final result
const ids.map(id => hashMap[id])
const data = [{key:"a"},{key:"d"},{key:"c"},{key:"b"}] // <-your data
const order = ["c", "a", "b", "d"] // <-create an array in the order you wish
const orderedArray = order.map(char=>data.find(res=>res.key===char)) // <- what you want
For each char in order: it will map if the char is equal to any key found within your data, and return it, consecutively