I have 2 List Objects, and need to remove all items from ListA that contains ListB and return the remaining. My approach is the following:
LIST A
[{
"id": 1,
"name": "ant"
}, {
"id": 2,
"name": "ant2"
}, {
"id": 3,
"name": "ant3"
}, {
"id": 4,
"name": "ant3"
}, {
"id": 5,
"name": "ant3"
}]
LIST B
[{
"id": 1,
"name": "ant"
}, {
"id": 4,
"name": "ant4"
}, {
"id": 3,
"name": "ant3"
} ]
What I have tried:
const xxx = this.listA.filter(x => !listB.includes(x) != null);
Note (Alternative scenario): When there are 2 List that are identical, the expected result is a []. However, in my case its same as a single List.
Here you are :)
const listBSerialized = listB.map(x => JSON.stringify(x))
const xxx = listA.filter(x => !listBSerialized.includes(JSON.stringify(x)));
You'll have the following result:
[ { id: 2, name: 'ant2' },
{ id: 4, name: 'ant3' },
{ id: 5, name: 'ant3' } ]
You can also use models ever you need properties order guarantee on the serialization, sometimes the objects can have the same properties but in a different order (it depends on where is the object coming from):
function Ant(id, name) {
this.id = id
this.name = name
}
listA = [
new Ant(1, "ant"),
new Ant(2, "ant2"),
new Ant(3, "ant3"),
new Ant(4, "ant3"),
new Ant(5, "ant3")
]
listB = [
new Ant(1, "ant"),
new Ant(4, "ant4"),
new Ant(3, "ant3")
]
const listBSerialized = listB.map(x => JSON.stringify(x))
const xxx = listA.filter(x => !listBSerialized.includes(JSON.stringify(x)));
Here is a simple one liner that will handle this task:
const xxx = listA.filter(base => ! listB.some(f => f.id === base.id && f.name === base.name))
where a is ListA and b is ListB array of objects. Basically what you would do is to filter the a array with a condition that there is no object that has the same id and name in b array.
Related
So I have an array like this. Like array containing an array of objects.
posts = [
[
{
"id": 2,
"info": "This is some information"
},
{
"id": 3,
"info": "This is the other information"
}
],
[
{
"id": 2,
"info": "This is a duplicated id I want to remove"
},
{
"id": 4,
"info": "This information is safe"
}
]
]
I want to get the elements from each array and create a new array that only has the objects at the same time removing duplicated ids.
What am trying to achieve is something like
posts = [
{
"id": 2,
"info": "This is some information"
},
{
"id": 3,
"info": "This is the other information"
},
{
"id": 4,
"info": "This information is safe"
}
]
This is the code I have so far
id = ids.map(val => {
for(let i in val) {
console.log(val)
}
return something
})
I keep getting undefined values. I have tried forEach, for loop.
Any help would be much appreciated. Thanks
Use flat to get a flattened array of objects, and then loop over the array. If the current object's id can't be found in an object in the output array push that object.
const posts=[[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
const out = [];
for (const obj of posts.flat()) {
const found = out.find(f => obj.id === f.id);
if (!found) out.push(obj);
}
console.log(out);
You could use .flat() and then .filter():
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const newPosts = posts.flat().filter((x, i, self) => i === self.findIndex((y) => x.id === y.id));
console.log(newPosts);
Another potential (and more optimal) solution could be this using .reduce():
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const newPosts = Object.values(posts.flat().reduce((acc, curr) => {
return {
...acc,
...(!acc[curr.id] ? { [curr.id]: curr } : undefined),
};
}, {}));
console.log(newPosts);
Or, if you don't like .reduce(), you can do something very similar with the Map object and a for...of loop:
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const map = new Map();
for (const item of posts.flat()) {
if (map.has(item.id)) continue;
map.set(item.id, item);
}
const newPosts = Array.from(map.values());
console.log(newPosts);
Or even use a classic for loop to get the job done:
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const flattened = posts.flat();
const map = {};
for (let i = 0; i < flattened.length; i++) {
if (map[flattened[i].id]) continue;
map[flattened[i].id] = flattened[i];
}
console.log(Object.values(map));
Either way, in each of these examples we're following the same workflow:
Flatten the array so that all items are on the same level.
Filter out the items with the duplicate IDs.
I group by id in order to remove duplicates.
var posts = [[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
var agg = {}
posts.forEach(function(arr) {
arr.forEach(function(item) {
agg[item.id] = agg[item.id] || item
})
})
console.log(Object.values(agg))
.as-console-wrapper {
max-height: 100% !important
}
Flatten the array with flat, then use a set to keep track of the ids we already have. The ternary inside the filter is logic to check if the id is already in the set, and if it is, we filter the item out. Otherwise, we add the id back to the set.
const posts = [[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
const flat = posts.flat();
const ids = new Set();
const filtered = flat.filter((item) => ids.has(item.id) ? false : ids.add(item.id));
console.log(filtered);
.as-console-wrapper {
max-height: 100% !important
}
There are two things we need to do:
Flatten the inner areas into one main array with array.prototype.flat()
Remove duplicates based on, I'm assuming, the order of their appearance in the data.
We can do this by reducing the flattened array to an object with a condition that doesn't add any present id's if they're found
Then we convert that object to an array using Object.values()
let posts = [ [ { "id": 2, "info": "This is some information" }, { "id": 3, "info": "This is the other information" } ], [ { "id": 2, "info": "This is a duplicated id I want to remove" }, { "id": 4, "info": "This information is safe" } ] ]
let flattened = posts.flat()
console.log('Flattened: ', flattened)
let unique = flattened.reduce((acc, obj) => {
if (!acc[obj.id]) {
acc[obj.id] = obj
}
return acc
}, {})
console.log('Unique Objects: ', unique)
let result = Object.values(unique)
console.log('Final Array: ' ,result)
Doing it in one go and with a spread ... object merge:
let posts = [ [ { "id": 2, "info": "This is some information" }, { "id": 3, "info": "This is the other information" } ], [ { "id": 2, "info": "This is a duplicated id I want to remove" }, { "id": 4, "info": "This information is safe" } ] ]
let result = Object.values(
posts.flat().reduce((acc, obj) =>
({...{[obj.id]: obj}, ...acc})
, {})
);
console.log('Final Array: ', result);
arr1 = [
{
"levelNumber": "2",
"name": "abc",
},
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
}
]
my result array should have objects with max levelNumber i.e 3 in this case.
it should look like:
resultArr = [
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
}
]
note that here levelNumber can be anything..
please help me with the generic nodejs code to get duplicate max value objects
You can first find the max level of all the objects in the array and then filter the array
arr1 = [
{
"levelNumber": "2",
"name": "abc",
},
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
}
]
const maxLevel = String(Math.max(...arr1.map(obj => Number(obj.levelNumber))))
const maxLevelObjects = arr1.filter(obj => obj.levelNumber === maxLevel)
console.log(maxLevelObjects);
const data = [
{
"levelNumber": "2",
"name": "abc",
},
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
}
];
const levelNumbers = data.map((item) => parseInt(item.levelNumber));
const maxLevelNumber = Math.max(...levelNumbers).toString();
const highestLevelItems = data.filter((item) => item.levelNumber == maxLevelNumber);
console.log(highestLevelItems);
/* output
[
{ levelNumber: '3', name: 'abc' },
{ levelNumber: '3', name: 'raks' }
]
*/
EDIT
As #nat mentioned in comment:
if I add one more object in the array, with name = 'raks & levelNumber = '4' then it should display maximum levelNumber wrt that particular name. i.e.
{ "levelNumber": "3", "name": "abc" }, { "levelNumber": "4", "name": "raks" }
To achieve this, you have to:
make a Set of names
make a separate empty array to hold final result
repeat the above process for each name and add result in the array
return complete result
const data = [
{
"levelNumber": "2",
"name": "abc",
},
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
},
{
"levelNumber": "4",
"name": "raks",
},
{
"levelNumber": "5",
"name": "raks",
}
];
// 1.
const names = new Set(data.map((item) => item.name)); // Set is used to get only unique items
// 2.
const result = []; // For normal JS
// const result: Array<{levelNumber: string, name: string}> = []; // For TS
// 3.
names.forEach((name) => {
/* minify data (filter items with only particular name) e.g. [{levelNumber: '2', name: 'abc'}, {levelNumber: '3', name: 'abc'}] */
const minifiedData = data.filter((item) => item.name === name);
/* same process, now for minified array */
const levelNumbers = minifiedData.map((item) => parseInt(item.levelNumber));
const maxLevelNumber = Math.max(...levelNumbers).toString();
minifiedData.forEach((item) => {
if (item.levelNumber == maxLevelNumber)
result.push(item); // push every matching item (item with highest level) in final result
});
});
// 4.
console.log(result);
const arr1 = [
{
levelNumber: '2',
name: 'abc',
},
{
levelNumber: '3',
name: 'abc',
},
{
levelNumber: '3',
name: 'raks',
},
];
const getHighLevelElements = (array) => {
if (array.length === 0) return null;
array.sort((elem1, elem2) => {
if (Number(elem1.levelNumber) < Number(elem2.levelNumber)) {
return 1;
}
if (Number(elem1.levelNumber) > Number(elem2.levelNumber)) {
return -1;
}
return 0;
});
return array.filter((elem) => elem.levelNumber === array[0].levelNumber);
};
const resultArr = getHighLevelElements([...arr1]);
console.log(resultArr);
I would first have a variable called highestLevel to store the highest level number found in the array of objects (will be used later while looping), loop through the whole array and checking every key levelNumber and storing that number IF highestLevel is lower than the value of the current object levelNumber.
After I've looped through the array and got the actual highestLevel number, I would loop through again and only get the objects that are equivalent to my variable highestLevel
You can just iterate one time over arr1 with Array.prototype.reduce()
Code:
const arr1 = [{levelNumber: '2',name: 'abc',},{levelNumber: '3',name: 'abc',},{levelNumber: '3',name: 'raks'}]
const result = arr1.reduce((a, c) => !a.length || +c.levelNumber === +a[0].levelNumber
? [...a, c]
: +c.levelNumber > +a[0].levelNumber
? [c]
: a,
[])
console.log(result)
i have such an array.
[
{
id: 1,
title: "one",
cats: [],
},
{
id: 2,
title: "two",
cats: [{ id: 3 }, { id: 4 }],
},
{
id: 3,
title: "sub 1",
cats: [],
},
{
id: 4,
title: "sub 2",
cats: [],
},
];
How can i correctly reference the id 3 and 4 to the nested array of cats.
I need to achive the following.
I need to display the list as buttons, but the ones that have nested to be dropdown.
example
one.title
two.title
sub1.title
sub2.title
I dont want to have id 3 and 4 data like title, into the nested array because the router takes ID as param, so basically when i will click on sub1 it should display data from id-3.
Please help me understand this as i am new.
Thank you.
You can use this function to fomat your array:
const formatArray = arr =>
arr.map(item => {
if (item.cats.length === 0) {
return item;
} else {
const itemtFormatted = { ...item };
itemtFormatted.cats = item.cats.map(cat =>
arr.find(e => e.id === cat.id)
);
return itemtFormatted;
}
});
You can check here: https://jsfiddle.net/x9o6dkum/
Compare two array of objects to find distinct values by key number
Suppose the old object consists of
oldChoices = [{"number": 1, "text": "abc" }, {"number": 2, "text": "pqr" }]
and new object consists of
newChoices = [{"number": 1, "text": "abc" }, {"number": 2, "text": "pqr" }, {"number": 3, "text": "xyz" }]
So need to get:
[{"number": 3, "text": "xyz" }]
Note:
1. Values populate in the newChoices array on the keypress event of the textbox.
2. newChoices can get value at the start as well.
Attempt 1:
var uniqueTemp = [];
$.each(oldChoices, function(x, e1){
$.each(newChoices, function(y, e2){
if(e1.number != e2.number){
uniqueTemp.push(e2);
}
});
})
Attempt 2:
var uniqueTemp = [];
oldChoices.filter(function(x){
if(newChoices.indexOf(x.number) === -1){
uniqueTemp.push(x);
return true;
}else{
return false;
}
});
Expected:
[{"number": 3, "text": "xyz" }]
You could take a Set and filter the new array.
var oldChoices = [{ number: 1, text: "abc" }, { number: 2, text: "pqr" }],
newChoices = [{ number: 1, text: "abc" }, { number: 2, text: "pqr" }, { number: 3, text: "xyz" }],
old = new Set(oldChoices.map(({ number }) => number)),
result = newChoices.filter(({ number }) => !old.has(number));
console.log(result);
Your second attempt is close, just change to:
newChoices.filter((x) => {
return (!oldChoices.find((choice) => choice.number === x.number));
});
Here is your solution . Simple use the flag for it .
in arr you will have a unique object as expected .
var oldChoices = [{"number": 1, "text": "abc" }, {"number": 2, "text": "pqr" }]
var newChoices = [{"number": 1, "text": "abc" }, {"number": 2, "text": "pqr" }, {"number": 3, "text": "xyz" }];
var arr = []
var flag = 0;
newChoices.forEach(function(newChoice){
oldChoices.forEach(function(oldChoice){
if(oldChoice.number == newChoice.number){
flag = 1;
}
});
if(flag != 1){
arr.push(newChoice);
}
flag = 0;
});
console.log(arr);
This is a generic function that calculates the difference of two arrays:
let arrayDifference = (v1, v2, cmp = null) =>
[...v1.filter(o1 => !v2.some(o2 => cmp ? cmp(o1, o2) : o1 === o2)),
...v2.filter(o1 => !v1.some(o2 => cmp ? cmp(o1, o2) : o1 === o2))]
Than you can invoke it with the right comparison function:
arrayDifference(
oldChoices,
newChoices,
(o1, o2) => o1.number === o2.number
)
This function finds the unique objects that occurs both in oldChoices and in newChoices.
I'm using underscore to extract some props into a separate object but the structure is not as I want:
let element = {
foo: 0,
bar: 1,
baz: _.map(
_.filter(element.properties, (prop) =>
_.contains(validationProps, prop.name)), (rule) =>
({ [rule.name]: rule.value }) )
}
.. returns an array of objects for baz:
[ {"required":true} , {"maxLength":242} ]
.. what I need however is:
{ "required":true, "maxLength":242 }
Or use JavaScript's Array.prototype.reduce()
The reduce() method executes a reducer function (that you provide) on each member of the array resulting in a single output value.
let data = [{
"name": "label",
"value": "Short 2"
},
{
"name": "required",
"value": true
},
{
"name": "maxLength",
"value": 242
}
];
let reformatted = data.reduce((pv, cv) => {
pv[cv.name] = cv.value;
return pv;
}, {});
console.log(reformatted);