Merge and combine item into array based on property - javascript

I am trying to merge an object into an array. Based on the title of the object, if it already exists in the array I want to add the amount of the object into the existing amount.
For example:
let items = [{"title":"x","amount":1}, {"title":"y","amount":1}, {"title":"z","amount":1}];
let obj1 = {"title":"x","amount":2};
If obj1 is merged into items the expected output would be
[{"title":"x","amount":3}, {"title":"y","amount":1}, {"title":"z","amount":1}]
Here is the solution I've come up with so far, Its working but I feel like there has to be a more elegant solution out there.
mergeResponses(x){
var found = this.items.some(function (arr) {
return arr.title === x.title;
});
if(!found){
//item doesnt exist, add to array
this.items.push(x);
}else{
//item already exists, add amount to existing amount
let dupItem = this.items.find(function (y) {
return y.title == x.title;
});
dupItem.amount += x.amount;
}
}

Below is a direct translation of your requirements. It's slightly more elegant than your implementation in the sense that the lookup only needs to be performed once.
let items = [{"title":"x","amount":1}, {"title":"y","amount":1}, {"title":"z","amount":1}];
let item = {"title":"x","amount":2};
let existing = items.find(i => i.title === item.title);
if (existing) {
existing.amount += item.amount;
} else {
items.push(item);
}
console.log(items);

you can do like this also,I don't say that this the better way but this also one way to do this.
var xyz=items.find(function(item){
if(item.title==obj1.title)
return item;
});
if(xyz)
xyz.amount+=obj1.amount;
else
items.push(obj1);

you can do as :
var items = [{"title":"x","amount":1}, {"title":"y","amount":1}, {"title":"z","amount":1}];
var obj = {"title":"x","amount":3};
matches = _.filter(items, i => i.title === obj.title)
if (matches) {
_.forEach (
matches,
match => match.amount += obj.amount
)
} else {
items.push(obj)
}
console.log(JSON.stringify(items))

Related

Push elements that don't have a specific class into an array

I tried to make a function looking for elements that don't have class "check", in this case I got it. Then I wanted to push them into an array so I got length, but what I got wasn't what I expected, do you have any solution for this case?
PLESAE READ MY COMMENTS IN THE CODES
What i wanted is to get the length of list that does not have class "check"
const countUnactiveList =()=> {
let list = [...todoList.children];
let listArr = [];
list.filter(child => {
if(!child.classList.contains('check')) {
console.log(child); //it works, i got the lists that does not have class "check"
listArr.push(child);
console.log(listArr.length); //the output are 1 2 3 creeping down. But it should be 3
}
});
}
Array.prototype.filter returns a new array and you're logging the length in each iteration.
here's the fixed code:
const countUnactiveList = () => {
const list = [...todoList.children];
const listArr = list.filter(child => {
return !child.classList.contains('check');
});
console.log(listArr, listArr.length);
return listArr.length;
}
based on the function name, I think you're just interested in the count. You can return the length of the filtered array.
An alternate approach using reduce:
const countUnactiveList = () => {
const list = [...todoList.children];
return list.reduce((count, child) => {
if(!child.classList.contains('check')) {
count += 1;
}
return count;
}, 0)
};

How to add second array objects in first array object based on id value using angular6?

This is my code
categories = [{"id":"101","name":"category1"},{"id":"102","name":"category2"},{"id":"103","name":"category3"},{"id":"104","name":"category4"}];
actions = [{"id":"201","name":"action1","category_id":"101"},{"id":"202","name":"action2","category_id":"101"},{"id":"203","name":"action3","category_id":"102"},{"id":"204","name":"action4","category_id":"104"}];
In the above categories array id value is existed in actions array. so i want to combine the two arrays into one array like the output as follows.
Output:-
finalList = [{"id":"101","name":"category1","actions":[{"id":"201","name":"action1","category_id":"101"},{"id":"202","name":"action2","category_id":"101"}]},{"id":"102","name":"category2","actions":[{"id":"203","name":"action3","category_id":"102"}]},{"id":"103","name":"category3","actions":[]},{"id":"104","name":"category4","actions":[{"id":"204","name":"action4","category_id":"104"}]}]
for each category find action elements and add then to category object
this.categories.forEach((element) => {
element['actions'] = this.actions.filter((data) => data.category_id === element.id);
});
console.log(this.categories);
use the map function along with the filter
var categories = [{"id":"101","name":"category1"},{"id":"102","name":"category2"},{"id":"103","name":"category3"},{"id":"104","name":"category4"}];
var actions = [{"id":"201","name":"action1","category_id":"101"},{"id":"202","name":"action2","category_id":"101"},{"id":"203","name":"action3","category_id":"102"},{"id":"204","name":"action4","category_id":"104"}];
var result = categories.map((item) => {
item.action = actions.filter( ac => item.id === ac. category_id)
return item;
})
console.log(result)
You can simply use Array.reduce() to create a map of actions ,group by category Id. And than you can use Array.map() on the categories to get the desired result. The overall time complexity of this solution will be O(n).
let categories = [{"id":"101","name":"category1"},{"id":"102","name":"category2"},{"id":"103","name":"category3"},{"id":"104","name":"category4"}];
let actions = [{"id":"201","name":"action1","category_id":"101"},{"id":"202","name":"action2","category_id":"101"},{"id":"203","name":"action3","category_id":"102"},{"id":"204","name":"action4","category_id":"104"}];
let map = actions.reduce((a,curr)=>{
(a[curr.category_id] = a[curr.category_id] || []).push(curr);
return a;
},{});
let result = categories.map((o)=>{
o.actions = map[o.id] || [];
return o;
});
console.log(result);
// Use simple for loop along with filter
categories = [{"id":"101","name":"category1"},{"id":"102","name":"category2"},{"id":"103","name":"category3"},{"id":"104","name":"category4"}];
actions = [{"id":"201","name":"action1","category_id":"101"},{"id":"202","name":"action2","category_id":"101"},{"id":"203","name":"action3","category_id":"102"},{"id":"204","name":"action4","category_id":"104"}];
for(var i=0;i<categories.length;i++){
categories[i]['actions'] = actions.filter((data) => data.category_id === categories[i].id);
};
console.log(categories)
I am using for loops working fine.
for(var i=0;i<categories.length;i++)
{
categories[i]["actions"]=[];
for(var j=0;j<actions.length;j++)
{
if(categories[i].id==actions[j].category_id){
categories[i]["actions"].push(actions[j]);
}
}
}
Any other approach without using for loops?

Js copy objects from array without reference

I hava a problem with copy a objects to array. I think it is an problem with reference.
In my program i have few array. First is dataForMonth - it is array of objects with data for month. And second is an products array which contains products objects. Product have property forecastArry wchich is array of objects.
Here code :
this.allProducts.map(function (product) {
var dataForMonth = data.filter(function (e) {
return e.dataId === product.productDataId;
});
var z = { posId: product.sales_plan_pos_id, arry: [] }
for (var sheetMonth of sheet.channels) {
var result = dataForMonth.filter(function (e) {
return e.CHANNEL === sheetMonth.CHANNEL;
});
product.forecastArry[someId].channels = result;
);
The problem is that the every changed channels property have the same value - its a value from last product?
Anybody know how to fix it ?
Seems like you want to edit each product in this.allProducts. So you want to add a return value to your map. You should also use let so that the scope of variables declared is preserved within map function, although I believe the map function already takes care of that. In addition, not that you have to reassign this.allProducts to your map function call. So your answer should be something like the following:
this.allProducts = this.allProducts.map(function (product) {
let dataForMonth = data.filter(function (e) {
return e.dataId === product.productDataId;
});
let channelsForMont = [];
let z = { posId: product.sales_plan_pos_id, arry: [] }
for (let sheetMonth of sheet.channels) {
let result = dataForMonth.filter(function (e) {
return e.CHANNEL === sheetMonth.CHANNEL;
});
product.forecastArry[someId].channels = channelsForMont;
return product;
);
P.S Your original code has some missing brackets and result variable is unused. You should do something about them.

Remove multiple elements from array

I'd like to remove multiple specific elements from my array before it displays. Here is the code I have but it results in none of the elements being displayed:
$('[data-fancybox]').on('click', function() {
var visibleLinks = $('.fancybox:visible');
$.fancybox.open( visibleLinks, {
//options go here
caption: function (instance, item) {
var caption, link, collectTags, tags, filterTags, filteredTags;
function format(tpl, binding) {
if (typeof binding != 'function') return format(tpl, function (_, name) {
return binding[name];
});
return tpl.replace(/\$(\w+)/g, binding);
}
caption = $(this).data('caption');
link = format('<br>See more pictures', item);
collectTags = $(this).parent().attr("class").split(' ');
function createTag(it) {
return format("<a href='$site$it'>$it</a>", {
site: (it == 'wedding' || it == 'concert') ? 'http://example.com/gallery/#filter=.' : 'http://example.com/gallery/#filter=.',
it: it
});
}
filterTags = ['churchevent', 'corporate'];
filteredTags = tags.filter(function(itm){return itm!== filterTags});
tags = $.map(collectTags, createTag);
return [].concat(caption ? [caption, link] : link).concat(filteredTags.slice(1).join(', ')).join('<br>');
}
}, visibleLinks.index( this ) );
return false;
});
I'm supposing that, since you wrote "remove multiple specific elements" you want to REMOVE filterTags.
If that's the case then change this:
filterTags = ['churchevent', 'corporate'];
filteredTags = tags.filter(function(itm){return itm!== filterTags});
tags = $.map(collectTags, createTag);
return [].concat(caption ? [caption, link] : link).concat(filteredTags.slice(1).join(', ')).join('<br>');
to this:
filterTags = ['churchevent', 'corporate'];
tags = $.map(collectTags, createTag);
filteredTags = tags.filter((item)=>{
for(tag in filterTags) if (item.indexOf(filterTags[tag]) != -1) return false;
return true;
});
return [].concat(caption ? [caption, link] : link).concat(filteredTags.slice(1).join(', ')).join('<br>');
else just use != -1 instead of == -1 in the filter method.
What is "tags" in the context of tags.filter? I'm assuming it is some array. In either case, your filter is checking that an item in tags is not equal to filterTags, an array. Of course a single item in an array won't be equal to an array, so this will always return true, thus not filtering anything.
I think you probably want something like:
filteredTags = tags.filter(function(itm){return filterTags.indexOf(itm) !== -1});
Are you speaking about this array?
filterTags = ['churchevent', 'corporate'];
filteredTags = tags.filter(function(itm){return itm!== filterTags});
// Of note, you are creating tags just below this code. Should you move it up?
// Or rename tags => collectionTags???
// Either way, the filter function you are using is not doing what you expect.
tags.filter(function(itm){
// itm will be whatever, I'm guessing a string of some sort like "churchevent"
// Now you are trying to compare a string like "churchevent" to the
// array filterTags.
// This is what is happening...
return itm !== ['churchevent', 'corporate'];
// What you want to do is this in ES5
return (filterTags.indexOf(itm) === -1);
// or this in ES6
return !filterTags.includes(itm);
// Note the bang in front.
// TRUE will put itm in the tags array, FALSE will not.
}
Also, please reference the filter function in MDN.
Filter Function (MDN)

Return Object in Array if Property Match

Here is the scenario:
There is a parameter titledlistOfSelectedProductIdsthat contains
all of the selected ids.
There is another list titled listOfAllPossibleProducts, which
contains a list of objects. That object contains a ProductId,
ProductName, and ProductCode. It looks something like this:
The task at hand:
I need to loop through my listOfSelectedProductIds. If the ProductId matches a ProductId from listOfAllPossibleProducts, then I need to return that object.
Here is what I am doing:
function SelectedProducts(listOfSelectedProductIds){
for (var index = 0; index < listOfSelectedProductIds.length; index++) {
var currentItem = listOfSelectedProductIds[index];
var desiredProduct = _.contains(listOfAllPossibleProducts, currentItem);
if (desiredProduct === true) {
return listOfAllPossibleProducts[index];
}
}
}
What's currently happening:
My loop is getting the selected id as expected i.e. currentItem, but _.contains(...)
always returns false.
Question:
What is the best way to find the objects in
listOfAllPossibleProducts that have ProductIds that match my
ProductIds in the listOfSelectedProductIds
How about using _.filter:
var result = _.filter(listOfAllPossibleProducts, function (el) {
return _.contains(listOfSelectedProductIds, el.id);
});
Or the non-underscore method:
var result = listOfAllPossibleProducts.filter(function (el) {
return listOfSelectedProductIds.indexOf(el.id) > -1;
});
DEMO
create another structure productsByProductId once!
var productsByProductId = {};
listOfAllPossibleProducts.forEach(p => {
productsByProductId[p.ProductId()] = p
});
and maybe a helper function
function getProductById(id){
return productsByProductId[id];
}
and use this to map the ids to the nodes
var selectedProducts = listOfSelectedProductIds.map(getProductById)

Categories