I am getting unexpected results from my below program. Both inputs and outputs arrays are getting same value. Somehow from this I got to know that it's the problem of passing a reference. My question is how can I achieve the same with below approach. Appreciate your time.
Expecting inputs array to be [[{id: 1, name: 'foo'}, {id: 2, name: 'bar'}], [{id: 1, name: 'foo'}]]
var inputs = [];
var outputs = [];
var exampleArr = [{id: 1, name: 'foo'}, {id: 2, name: 'bar'}];
function analyze(listArr) {
inputs.push(...listArr);
outputs = [...outputs, ...listArr];
outputs.forEach((item) => {
item.name = 'hello';
});
console.log('inputs', inputs);
console.log('ouputs', outputs);
listArr.pop();
if(listArr.length) analyze(listArr);
}
analyze(exampleArr);
Clone all objects in listArr (one nesting level only) before appending to outputs.
var inputs = [];
var outputs = [];
var exampleArr = [{id: 1, name: 'foo'}, {id: 2, name: 'bar'}];
function analyze(listArr) {
inputs.push(...listArr);
// clone objects in array, one level
outputs = [...outputs, ...listArr.map(x=>typeof x==='object'? {...x}:x)];
outputs.forEach((item) => {
item.name = 'hello';
});
console.log('inputs', inputs);
console.log('ouputs', outputs);
listArr.pop();
if(listArr.length) analyze(listArr);
}
analyze(exampleArr);
Related
Let suppose I have two array
let array1 = [{id: 1,name: "a"},{id: 2,name: "b"}]
let array2 = [{id: 1,name: 'c'},{id: 3,name: 'd'}]
and I want the resulting array to be like so
let result = [{id: 1,name: 'c'},{id: 2,name: 'b'},{id: 3,name: 'd'}]
So if the second array has an object and the first array also has an object with the same id then replace the first object with the second array object.
Currently, I have try below code but it checks for each value to match and I want to match on id based only
const uniqueArray = this.servicesdata.filter((item, index) => {
const _item = JSON.stringify(item);
return (
index ===
this.servicesdata.findIndex(obj => {
return JSON.stringify(obj) === _item;
})
);
});
console.log(uniqueArray);
Here's a O(NlogN) approach.
let array1 = [{id: 1,name: "a"},{id: 2,name: "b"}] // Let's say its size is N1
let array2 = [{id: 1,name: 'c'},{id: 3,name: 'd'}] // Let's say its size is N2
let duplicate = new Set() // A set to check for duplicate elements
let result = array2
array2.forEach((item)=>{ // O(N2logN2)
duplicate.add(item.id);
})
array1.forEach((item)=>{ // O(N1logN2)
// If `duplicate` does NOT have `item.id`, that means it's unique in array1, so add it to result
// Otherwise, skip it because for duplicates, we want value from array2
if(!duplicate.has(item.id))
result.push(item);
})
// Overall complexity of approach - O(N2logN2) + O(N1logN2) ==> O(NlogN)
console.log(result);
Output:
[ { id: 1, name: 'c' },
{ id: 3, name: 'd' },
{ id: 2, name: 'b' } ]
With lodash you can use unionBy:
let array1 = [{id: 1,name: "a"},{id: 2,name: "b"}]
let array2 = [{id: 1,name: 'c'},{id: 3,name: 'd'}]
console.log(_.unionBy(array2, array1, 'id'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
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 two arrays:
array a:
var a = [
{
id: 1,
name: 'a'
},
{
id: 2,
name: 'b'
},
{
id: 3,
name: 'c'
}
];
array ids:
var ids = [1];
I want to array a filtered by array ids, result i wanted:
var a = [
{
id: 1,
name: 'a'
}
];
The most important thing is i want the change on the original array, rather than return a new array.
underscore solution is better:)
You can use .filter
a = a.filter(function(el){
return ~ids.indexOf(el.id)
});
// should give you [{id: 1, name: 'a'}]
Today I tried to solve similar task (filtering the original array of objects without creating a new array) and this is what I came up with:
const a = [{ id: 1, name: 'a'}, { id: 2, name: 'b'}, { id: 3, name: 'c'}];
const ids = [1];
Array.from(Array(a.length).keys()).reverse().forEach(index =>
!ids.some(id => id === a[index].id) && a.splice(index, 1)
);
console.log(a); // [{ id: 1, name: 'a'}]
The point is that we need to loop back through the original array to be able to use Array.prototype.splice, but I didn't want the for-loop, I wanted to have ES6 one-liner. And Array.from(Array(a.length).keys()).reverse() gives me a list of reversed indexes of the original array. Then I want to splice the original array by current index only if the corresponding item's id is not present in the ids array.
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.