I have an array with some objects
var arr = [{index: 1, type: 2, quantity: 1}, {index: 3, type: 1, quantity: 2}, {index: 1, type: 3, quantity: 3}];
Now I want to search my array if exists an object inside it with a given index and type. If exists, I add + 1 to the quantity property. If not, I add a new object with quantity of 1. I tried to use, $.grep and $.inArray but to no avail. What is the best way to search the properties in an array of objects?
tnx!
For loop with if condition: JsFiddle
var arr = [{index: 1, type: 2}, {index: 3, type: 1}];
var found = '';
for(item in arr){
if(arr[item].index === 1 && arr[item].type === 2){
found = arr[item];
}
}
In the function in grep you need to return the result of the test and also the result returned from grep is a new array. It does not modify the existing array.
I made a snippet:
var arr = [{index: 1, type: 2}, {index: 3, type: 1}, {index: 1, type: 3}];
var result = $.grep(arr, function(e){
return e.index === 1 && e.type === 3
});
alert(result[0].index + " " + result[0].type);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Related
I have array of objects like this:
[{key: 4214, value: 'example'}, ...etc
And i have some kind of template, that i want to use for generate a new array for objects.
{type: 1, blocks: []} //one item in blocks
{type: 2, blocks: []} //two items in blocks arr
{type: 3, blocks: []} //four items in blocks arr
I need to create an array based on a template, using data from an existing array.
In each of the templates types a limited specified number of objects can fit into the blocks object. When the loop creates all three types from the template, it must start over from type one. In type: 1 one object, two in type: 2, and four in type: 3
For example, the effect I want to achieve is the creation of new array from this [{key: 1}, {key: 2}, {key: 3}]
to this [{type: 1, blocks: [{key: 1}]}, {type: 2, blocks: [{key: 2}, {key: 3}]}]
If there were more objects, the next one object in new array will be type: 3, which blocks will contain 4 elements. And then, it starts again from type: 1. As a result, an array of any size must be transformed according to the logic of the template. sorry for my language, this is a super complicated explanation for me. if something is not clear, I will answer almost instantly
You can implement based on 7 items as one unit as follows.
const input = [{key: 1}, {key: 2}, {key: 3}, {key: 4}, {key: 5}, {key: 6}, {key: 7}, {key: 8}, {key: 9}];
const result = [];
for (let i = 0; i < input.length; i += 7) {
const item = [];
// Add Type1
item.push({
type: 1,
blocks: [ input[i] ]
});
// Add Type2
if (i + 1 < input.length) {
item.push({
type: 2,
blocks: [ input[i + 1] ]
});
}
if (i + 2 < input.length) {
item[1].blocks.push(input[i + 2]);
}
// Add Type3
if (i + 3 < input.length) {
item.push({
type: 3,
blocks: [ input[i + 3] ]
});
}
for (let subI = i + 4; subI < Math.min(i + 7, input.length); subI ++) {
item[2].blocks.push(input[subI]);
}
result.push(...item);
}
console.log(result);
I am facing problem to merge the two arrays. I have two arrays of objects first is prev having old values and another with updated values. I would like to have result array with all the objects of prev array with its updated value in array next, and also have objects in next array.
Example:
var prev = [{id: 1, val: 'abc'}, {id: 2, val: 'pqr'}];
var next = [{id: 1, val: 'nextVal'}, {id: 3, val: 'xyz'}];
expected
mergeOutput = [
{id: 1, val: 'nextVal'}, // value is updated
{id: 2, val: 'pqr'},
{id: 3, val: 'xyz'}
]
Note: Array order do not matter.
You can use Map() to merge array.
var prev = [{id: 1, val: 'abc'}, {id: 2, val: 'pqr'}];
var next = [{id: 1, val: 'nextVal'}, {id: 3, val: 'xyz'}];
var hash = new Map();
prev.concat(next).forEach(function(obj) {
hash.set(obj.id, Object.assign(hash.get(obj.id) || {}, obj))
});
var mergedArray = Array.from(hash.values());
console.log(mergedArray);
Source : StackOverflow
I have an array which is dynamically created by selecting items from a list:
[2, 4]
I also have an array of objects:
[{id: 1, name: "Param1"}, {id: 2, name: "Param2"}, {id: 3, name: "Param3"}, {id: 4, name: "Param4"}]
What I need to do is use the values in the first array to match against the ids in the objects in the second array and return those objects.
Help with this would be much appreciated
Thanks for your time
You can use this ES6 code, which turns the first array to a Set to allow fast lookup, and then applies the Array filter method, specifically intended for this purpose:
var select = [2, 4];
var data = [{id: 1, name: "Param1"}, {id: 2, name: "Param2"},
{id: 3, name: "Param3"}, {id: 4, name: "Param4"}]
var selectSet = new Set(select);
var result = data.filter( obj => selectSet.has(obj.id) );
console.log(result);
You can just use for loop as Liam's comment, or you can use the filter method of array like this:
var keys = [2, 4];
var objs = [{id: 1, name: "Param1"}, {id: 2, name: "Param2"}, {id: 3, name: "Param3"}, {id: 4, name: "Param4"}];
function filterById(obj) {
return keys.indexOf(obj.id) != -1;
}
var newArr = objs.filter(filterById);
The newArr is the result you want.
var data = [
{id: 1, quantity: 10, category: 'A'},
{id: 2, quantity: 20, category: 'B'},
{id: 1, quantity: 30, category: 'A'},
{id: 1, quantity: 30, category: 'Z'},
{id: 2, quantity: 40, category: 'D'}
];
var totalPerType = {};
for (var i = 0, len = data.length; i < len; ++i) {
totalPerType[data[i].id] = totalPerType[data[i].id] || 0;
totalPerType[data[i].id] += data[i].quantity;
}
var out = _.map(totalPerType, function (id, quantity) {
return {'id': id, 'quantity': quantity};
});
console.log(out);
My code currently sums the quantity for objects with the same id. It returns
[ {id:1, quantity:70}, {id:2, quantity:60} ]
How do I get the sum for objects based on both id and category?
For example, I need output like this:
[
{id:1, quantity:40, category:A},
{id:1, quantity:30, category:Z},
{id:2, quantity:20, category:B},
{id:, quantity:40, category:D}
]
I'd like an answer for both plain javascript and underscore.
Using vanilla js.
var tmp = {}
data.forEach(function (item) {
var tempKey = item.id + item.category;
if (!tmp.hasOwnProperty(tempKey)) {
tmp[tempKey] = item;
} else {
tmp[tempKey].quantity += item.quantity;
}
});
var results = Object.keys(tmp).map(function(key){
return tmp[key];
});
Note that this will change objects in original data. Would need to copy item when adding to the tmp object if that is unwanted
var data = [
{id: 1, quantity: 10, category: 'A'},
{id: 2, quantity: 20, category: 'B'},
{id: 1, quantity: 30, category: 'A'},
{id: 1, quantity: 30, category: 'Z'},
{id: 2, quantity: 40, category: 'D'}
];
var tmp = {}
data.forEach(function (item) {
var tempKey = item.id + item.category;
if (!tmp.hasOwnProperty(tempKey)) {
tmp[tempKey] = item;
} else {
tmp[tempKey].quantity += item.quantity
}
});
var results = Object.keys(tmp).map(function(key){
return tmp[key];
});
document.body.innerHTML ='<pre>' + JSON.stringify(results,null,4) +'</pre>';
Two good answers already but I thought I would show a ES6 solution that is a little more flexible. It uses Map and creates a unique key for each record by creating a string from the properties that need to be matched. Adds the records to the map with its key and sums values as required.
When done it converts the map to an array and returns the new array;
var data = [ // test data
{id: 1, quantity: 10, category: 'A'},
{id: 2, quantity: 20, category: 'B'},
{id: 2, quantity: 20, category: 'Z'},
{id: 2, quantity: 20, category: 'D'},
{id: 1, quantity: 30, category: 'A'},
{id: 1, quantity: 30, category: 'Z'},
{id: 2, quantity: 40, category: 'D'}
];
// function to collapse records in an array of objects;
// arr is the array of objects
// match is an array of property names that need to be matched
// sum us an array of property names that need to be summed
function collapse ( arr, match, sum ) { // bad name but just can't remember what this should be called
// define vars
var newArray, key, processRecord
// define function
// function to process each record
processRecord = function( item ) {
// define vars
var key, getKey, sumFields, record;
// define functions
getKey = function ( field ) { key += item[field]; } // Creates a key
sumFields = function ( field ) { record[field] += item[field];} // sums fields
// code
key = ""; // create a blank key
match.forEach( getKey ); // create a unique key
if(newArray.has( key ) ){ // does it exist
record = newArray.get( key ); // it does so get the record
sum.forEach( sumFields ); // sum the fields
}else{
newArray.set( key, item ); // the key does not exist so add new record
}
}
// code
newArray = new Map(); // create a new map
arr.forEach( processRecord ); // process each record
return ( [...newArray.values()] ); // convert map to array and return it
}
// call the function matching id and category summing quantity
var a1 = collapse( data, ["id" , "category"], ["quantity"] );
// call the function matching id only summing quantity
var a2 = collapse( data, ["id"], ["quantity"] );
// call the function matching category only summing quantity
var a3 = collapse( data, ["category"], ["quantity"] );
// call the function matching all fields and summing quantity
var a4 = collapse( data, ["id, "quantity", "category"], ["quantity"] );
Here's a solution using underscore:
// used when calling reduce to sum the quantities of a group of items
var sumQuantity = function(total, item){
return total + item.quantity;
}
// used by groupBy to create a composite key for an item
var itemKey = function(item){
return item.id + '/' + item.category;
}
// used to convert a group of items to a single item
var groupToSummedItem = function(group){
return {
id: group[0].id,
category: group[0].category,
quantity: _.reduce(group, sumQuantity, 0)
}
}
// the bit that does the work
var result = _.chain(data)
.groupBy(itemKey)
.map(groupToSummedItem)
.value();
I have a stupid problem that at first seems to be simple to solve, but turns out to be tricky.
I have an array of objects, each with two properties: id and value:
[
{id: 2, value: 10},
{id: 4, value: 3},
{id: 2, value: 2},
{id: 1, value: 15}
]
I want to write an algorithm that sums up the values of ones with similar id.
My end result should be a new array with only the merged objects:
[
{id: 2, value: 12},
{id: 4, value: 3},
{id: 1, value: 15}
]
I've tried the following, but it doesn't work:
var arr = [];
arr.push({id: 2, visit:10});
arr.push({id: 4, visit:3});
arr.push({id: 2, visit:2});
arr.push({id: 1, visit:15});
// Deep copy
var copy = jQuery.extend(true, [], arr);
var masterArr = [];
for (var i = 0; i < arr.length; i++) {
var objArr = [];
objArr.push(arr[i]);
for (var j = copy.length-1; j > -1; j--) {
if (arr[i].id === copy[j].id) {
var q = copy.splice(j,1);
}
}
masterArr.push(objArr);
}
My plan was to first gather all similar objects in separate arrays (objArr), sum them up and put them in an end array (masterArr). I use jquerys extend to make a deep copy (not a reference) and reverse iteration and splice to remove objects thats already been found as "duplicates".
This doesn't work! And it doesn't seem to be a very efficient mehtod to solve my problem.
How could I do this? Performance isn't top priority but rather "nice to have"!
Thanks!
You can do it like this:
// Assuming:
a = [{id: 2, value: 10}, {id: 4, value: 3}, {id: 2, value: 2}, {id: 1, value: 15}]
var b = {}, // Temporary variable;
c = []; // This will contain the result;
// Build a id:value object ( {1: 15, 2: 12, 4: 3} )
a.map(function(current){b[current.id] = (b[current.id] || 0) + current.value});
for(var key in b){ // Form that into the desired output format.
c.push({id: parseInt(key, 10), value: b[key]});
}
console.log(c);
/* [{id: 1, value: 15},
{id: 2, value: 12},
{id: 4, value: 3}] */
I'm using parseInt(key, 10), since the keys are strings, you'll probably want them converted to integers again.
// First group the data based on id and sum the values
var temp = data.reduce(function(result, current) {
result[current.id] = (result[current.id] || 0) + current.value;
return result;
}, {});
// then recreate the objects with proper id and value properties
var result = [];
for (var key in temp) {
result.push({
id: parseInt(key, 10),
value: temp[key]
});
}
console.log(result);
Output
[ { id: 1, value: 15 },
{ id: 2, value: 12 },
{ id: 4, value: 3 } ]
The quickest approach loops over the array only once using Array.prototype.filter():
var tmp = {},
result = arr.filter(function (el) {
if (tmp.hasOwnProperty(el.id)) {
tmp[el.id].visit += el.visit;
return false;
}
else {
tmp[el.id] = el;
return true;
}
});
It also reuses the objects, though this renders the original array to contain inaccurate values. If this is a problem, you can modify the example to copy each object property to a new object.