JavaScript array of objects contains every element of another array - javascript

I have an array of objects (array1).
I want to filter every element, that includes ALL of the tags ([1, 2, 3).
So the result should be id 1. But i cannot make it work. The results i get are id 1, 2 ,4 and i do not understand why exactly it acts like this.
let array1 = [
{ id: 1, tags: [1, 2, 3] },
{ id: 2, tags: [2, 3] },
{ id: 3, tags: [0, 3] },
{ id: 4, tags: [1, 3] }
];
let tags = [1, 2, 3];
let includesAll = array1.filter((a1) =>
a1.tags.every((tag) => tags.includes(tag))
);
console.log(includesAll);

You should do the opposite. Instead of verifying that every value in a1.tags is also in tags, you'll want to verify that every value in tags is also in a1.tags:
let includesAll = array1.filter((a1) =>
tags.every((tag) => a1.tags.includes(tag))
);
let array1 = [
{ id: 1, tags: [1, 2, 3] },
{ id: 2, tags: [2, 3] },
{ id: 3, tags: [0, 3] },
{ id: 4, tags: [1, 3] }
];
let tags = [1, 2, 3];
let includesAll = array1.filter((a1) =>
tags.every((tag) => a1.tags.includes(tag))
);
console.log(includesAll);

Related

How to replace duplicate objects from array

I know there are multiple ways to remove duplicates from arrays in javascript, the one i use is
let originalArray = [1, 2, 3, 4, 1, 2, 3, 4]
let uniqueArray = array => [...new Set(array)]
console.log(uniqueArray) -> [1, 2, 3, 4]
what i want is something similar but instead of removing the duplicates, to replace it with whatever string or number i want, like this
console.log(uniqueArray) -> [1, 2, 3, 4, "-", "-", "-", "-"]
this has to work with any order, like
[1, 2, 3, 3, 4, 5, 7, 1, 6]
result -> [1, 2, 3, "-", 4, 5, 7, "-", 6]
i tested this solution
const arr = [1, 2, 3, 1, 2, 3, 2, 2, 3, 4, 5, 5, 12, 1, 23, 4, 1];
const deleteAndInsert = uniqueList => {
const creds = uniqueList.reduce((acc, val, ind, array) => {
let { count, res } = acc;
if (array.lastIndexOf(val) === ind) {
res.push(val);
} else {
count++;
};
return { res, count };
}, { count: 0, res: [] });
const { res, count } = creds;
return res.concat(" ".repeat(count).split(" "));
};
console.log(deleteAndInsert(arr));
but only adds it at the end of the uniques, and also, only works with numbers
i want it to work with strings too, like dates as an example
["2021-02-22", "2021-02-23", "2021-02-22", "2021-02-28"]
You could still use a Set and check if the value is in the set.
const
unique = array => array.map((s => v => !s.has(v) && s.add(v) ? v : '-')(new Set));
console.log(...unique([1, 2, 3, 4, 1, 2, 3, 4]));
console.log(...unique([1, 2, 3, 3, 4, 5, 7, 1, 6]));
Just create new Array, use 1 set to control which element appeared, if one element appears more than 1, push new one character like '-'
let originalArray = [1, 2, 3, 4, 1, 2, 3, 4];
let newArray = [];
let set = new Set();
for (let i = 0; i < originalArray.length; i++) {
if(!set.has(originalArray[i])) {
newArray.push(originalArray[i]);
set.add(originalArray[i]);
} else {
newArray.push('-');
}
}
console.log(newArray);
You could do it with reduce
const dashDupes = array => array.reduce((acc, e) => {
if(acc.idx[e])
acc.arr.push('-')
else{
acc.arr.push(e);
}
acc.idx[e] = true;
return acc;
},{idx:{},arr:[]}).arr
console.log(...dashDupes([1, 2, 3, 4, 1, 2, 3, 4]))
console.log(...dashDupes([1, 2, 3, 3, 4, 5, 7, 1, 6]))
This is a very simple approach to the problem:
function uniqueReplace(arr, rep) {
let res = [];
for (x of arr) {
res.push(res.includes(x) ? rep : x);
}
return res;
}
console.log(...uniqueReplace([1, 2, 3, 4, 1, 2, 3, 4], '-'));
console.log(...uniqueReplace([1, 2, 3, 3, 4, 5, 7, 1, 6], '-'));

using array as key to loop through objects in JavaScript

I am learning JavaScript and have spent a good deal of time practicing looping through arrays and arrays of Objects. I wanted to learn how to use an array as a filter on an array of Objects. I couldn't find any articles that explained how to do this, so I had a go myself using a nested loop. However, I cannot get it to work.
Var catKey[]; is the array holding the data I want to use to filter through var posts[]; , identify which objects have a match in the Property cat: [] and return the title properties. I know I could use array.Filter but I want to be able to do this on the assumption I wont always know the number of items in the catKey array. The use case would be for a situation where I use an event handler that when a link I add is clicked on a Post in WordPress and returns the category Ids, I would then search through the list of Posts to find others that have the same category Ids. Can anyone tell me where I am going wrong.
var catKey = [2, 6];
var posts = [
{
id: 1,
cat: [1, 2, 3],
title: "Hello World"
},
{
id: 2,
cat: [5, 6, 7],
title: "Hello JavaScript"
},
{
id: 3,
cat: [8, 9],
title: "Hello Arrays!"
}
];
for (var i = 0; i < catKey.length; i++) {
for (var j = 0; j < posts.length[i]; j++) {
if (catKey[i] === posts[j].cat) {
document.write(posts[j].title);
}
}
}
To find the first entry to match your conditions you can make use of Array.prototype.find() function:
var catKey = [2, 6];
var posts = [
{ id: 1, cat: [1, 2, 3], title: "Hello World" },
{ id: 2, cat: [5, 6, 7], title: "Hello JavaScript" },
{ id: 3, cat: [8, 9], title: "Hello Arrays!" }
];
const resObj = posts
.find(p => p.cat.some(c => catKey.includes(c)))
.title;
console.log(resObj)
Or to find all, use Array.prototype.filter():
var catKey = [2, 6];
var posts = [
{ id: 1, cat: [1, 2, 3], title: "Hello World" },
{ id: 2, cat: [5, 6, 7], title: "Hello JavaScript" },
{ id: 3, cat: [8, 9], title: "Hello Arrays!" }
];
const resObjs = posts
.filter(p => p.cat.some(c => catKey.includes(c)))
.map(o => o.title);
resObjs.forEach((t) => console.log(t));
Based on your question, I assume catKey contains a whitelist of numbers that the nested cat array should match, i.e. as long as any value in the cat array is found in catKeys, you want to keep them.
In that case, you can simply use .filter() to iterate through all the posts, and evaluate if there is any intersection between the individual post's cat array against the whitelist:
var filteredPosts = posts.filter(function(post) {
return post.cat.filter(function(c) { return catKey.indexOf(c) !== -1; }).length;
});
If you want to try and write in ES6, that's also not a problem: and it's even more concise!
const filteredPosts = posts.filter(post => post.cat.filter(c => catKey.includes(c)).length);
See proof-of-concept below:
var catKey = [2, 6];
var posts = [{
id: 1,
cat: [1, 2, 3],
title: "Hello World"
},
{
id: 2,
cat: [5, 6, 7],
title: "Hello JavaScript"
},
{
id: 3,
cat: [8, 9],
title: "Hello Arrays!"
}
];
var filteredPosts = posts.filter(function(post) {
return post.cat.filter(function(c) { return catKey.indexOf(c) !== -1; }).length;
});
console.log(filteredPosts);
You can use map and find together to check the values in the array with the values in the array of objects. Using map the catKey array is iterated and for every element find is used to find that element in the array inside the object inside the array named post using the .includes() method.
var catKey = [2, 6];
var posts = [{
id: 1,
cat: [1, 2, 3],
title: "Hello World"
},
{
id: 2,
cat: [5, 6, 7],
title: "Hello JavaScript"
},
{
id: 3,
cat: [8, 9],
title: "Hello Arrays!"
}];
console.log(catKey.map((e) => posts.find((x) => x.cat.includes(e))))
var catKey = [2, 6];
var posts = [
{
id: 1,
cat: [1, 2, 3],
title: "Hello World"
},
{
id: 2,
cat: [5, 6, 7],
title: "Hello JavaScript"
},
{
id: 3,
cat: [8, 9],
title: "Hello Arrays!"
}
];
var result = posts.filter(({cat})=>{
return catKey.filter((key)=>{
return cat.includes(key)
}).length > 0
})
console.log(result);
short version
posts.filter(({ cat }) => catKey.filter(key => cat.includes(key)).length > 0);

Group array of objects depending on values

I have an array of objects
[
{ values: [2, 7, 1] },
{ values: [1, 2, 7] },
{ values: [7, 1, 2] },
{ values: [3, 4, 5] },
{ values: [2, 1, 8] },
{ values: [2, 1, 8] },
]
I want to group these objects together with the other object with same values. So this array of objects should be grouped into 3 groups since the first 3 objects have the same values, the next object is alone and the last 2 objects have the same values.
As seen in the example, the values can have different orders, but should still be considered the same.
I am thinking about for each element to loop through the remaining elements and see if they are alike, but it will result in O(n^2).
I guess I should remove elements from the array that has already been grouped.
So how could I, efficiently, group it as
[[first, second, third],[fourth],[fifth,sixth]]
Something like this?
var data = [
{ values: [2, 7, 1] },
{ values: [1, 2, 7] },
{ values: [7, 1, 2] },
{ values: [3, 4, 5] },
{ values: [2, 1, 8] },
{ values: [2, 1, 8] },
];
var hash = {};
for(var obj of data) {
var key = obj.values.sort().join("-");
if (!hash[key]) hash[key] = [];
hash[key].push(obj);
}
var result = [];
for(var k in hash) result.push(hash[k])
console.log(result)
Or js6 variant:
var data = [
{ values: [2, 7, 1] },
{ values: [1, 2, 7] },
{ values: [7, 1, 2] },
{ values: [3, 4, 5] },
{ values: [2, 1, 8] },
{ values: [2, 1, 8] },
];
var hash = data.reduce((hash, obj) => {
const key = obj.values.sort().join("-");
if (!hash[key]) hash[key] = [];
hash[key].push(obj);
return hash;
}, [])
var result = Object.keys(hash).map(k => hash[k])
console.log(result)
You can do this with forEach() loop and sort()
var arr = [
{ values: [2, 7, 1] },
{ values: [1, 2, 7] },
{ values: [7, 1, 2] },
{ values: [3, 4, 5] },
{ values: [2, 1, 8] },
{ values: [2, 1, 8] },
];
var result = [];
arr.forEach(function(e) {
var s = [].concat(e.values).sort().join('|');
if (!this[s]) {
this[s] = [e.values];
result.push(this[s]);
} else {
this[s].push(e.values)
}
})
console.log(result)

Concat multi-dimensional array into one-dimensional array

I have an array with objects
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ];
I want a new array [ 1, 2, 3, 1, 2, 3 ].
I have tried
nodes.map(node => node.children);
but it gives me [ [ 1, 2, 3 ], [ 1, 2, 3 ] ].
I have tried
[].concat(nodes.map(node => node.children));
but it doesn't work since it is just concatenating [] with [ [ 1, 2, 3 ], [ 1, 2, 3 ] ] which is just [ [ 1, 2, 3 ], [ 1, 2, 3 ] ].
You could use Array#reduce
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ],
result = nodes.reduce((r, node) => r.concat(node.children), []);
console.log(result);
console.log([... new Set(result)]); // for unique values
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do this with Array#reduce
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ];
var result = nodes.reduce(function(r, o) {
r = r.concat(o.children);
return r;
}, []);
console.log(result)
Another way to do this using Array#forEach:
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ]
final = []
nodes.forEach(x => final = final.concat(x.children))
console.log(final)
Another shorter way is (a little modification to what OP was trying to do):
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ];
var result = [].concat.apply([], nodes.map(x => x.children))
console.log(result);

underscore js mapping array of objects multiple properties to new array

var items = [{
//other properties... above
item_name: [
[1],
[2, 3]
],
item_description: [
[1],
[3, 4]
],
item_quantity: [
[1],
[4, 5]
],
item_value: null,
}, {
//other properties... above
item_name: 1,
item_description: 2,
item_quantity: 3,
item_value: 4,
}, {
//other properties... above
item_name: [1, 2, 3],
item_description: [1, 2, 3],
item_quantity: [1, 2, 3],
item_value: [1, 2, 3],
}];
var itemList = [];
items.forEach(function(item) {
if (!_.isArray(item.item_name)) {
itemList.push({
name: item.item_name,
description: item.item_description,
quantity: item.item_quantity,
value: item.item_value
});
}
var names = item.item_name ? _.flatten(item.item_name) : [];
var descriptions = item.item_description ? _.flatten(item.item_description) : [];
var quantity = item.item_quantity ? _.flatten(item.item_quantity) : [];
var values = item.item_value ? _.flatten(item.item_value) : [];
names.forEach(function(name, index) {
itemList.push({
name: names[index],
description: descriptions[index],
quantity: quantity[index],
values: values[index]
});
})
});
console.log(itemList);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.1/underscore-min.js"></script>
is there a way I can perform this faster in underscore, to remove all of the flattens?
for each item in the array I am taking
item_name[i]
item_description[i]
item_quantity[i]
item_value[i]
and adding them to the itemList
item properties in items can be [[],[]] or [] or integer or null
currently it is outputting what is expected (unless a name is null and it can skip items) however I do not like all of the loops this is performing and I am wondering if I can make a better use of underscore library
You can use this:
var myKeys = ['name', 'description', 'quantity', 'value'];
var result = _.flatten(items.map(function(item) {
return _.zip.apply(_, myKeys.map(function(key) {
return _.flatten([item['item_'+key]]);
})).map(function(arr) {
return _.object(myKeys, arr);
});
}));
Demo:
var items = [{
//other properties... above
item_name: [
[1],
[2, 3]
],
item_description: [
[1],
[3, 4]
],
item_quantity: [
[1],
[4, 5]
],
item_value: null,
}, {
//other properties... above
item_name: 1,
item_description: 2,
item_quantity: 3,
item_value: 4,
}, {
//other properties... above
item_name: [1, 2, 3],
item_description: [1, 2, 3],
item_quantity: [1, 2, 3],
item_value: [1, 2, 3],
}];
var myKeys = ['name', 'description', 'quantity', 'value'];
var result = _.flatten(items.map(function(item) {
return _.zip.apply(_, myKeys.map(function(key) {
return _.flatten([item['item_'+key]]);
})).map(function(arr) {
return _.object(myKeys, arr);
});
}));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.1/underscore-min.js"></script>

Categories

Resources