I have array like this:
let arr = [
{ x: 31, y: 8 }, // get 1
{ x: 32, y: 8, monster: { is: true, id: '19216' } }, // get special
{ x: 32, y: 9 },
{ x: 32, y: 10 },
{ x: 32, y: 11 }, // get 4
{ x: 32, y: 12 },
{ x: 32, y: 13 },
{ x: 32, y: 14 },
{ x: 32, y: 15 }, // get 8
{ x: 32, y: 16 } // get last
];
what I want to achieve is to get every fourth, get special one (the one with monster object) and also last one. So the output would be
[
{x: 31, y: 8},
{x: 32, y: 8, monster: { is: true, id: '19216' } },
{x: 32, y: 11},
{x: 32, y: 15},
{x: 32, y: 16}
]
It was easy to get every fourth and last one like this:
let arrThinned = [];
for (let i = 0; i < arr.length; i = i + 4) {
arrThinned.push({
x: arr[i].x,
y: arr[i].y,
});
}
if((arr.length - 1) % 4 !== 0) {
/* add also last one if not already added */
arrThinned.push({
x: arr[arr.length - 1].x,
y: arr[arr.length - 1].y
});
};
but I can't figure out how to additionally check if there is this special one beetwen every fourth and also add it to thinnedArr array. I need to keep the order. Demo of above code.
Here, use filter
let arr = [
{ x: 31, y: 8 }, // get 1
{ x: 32, y: 8, monster: { is: true, id: '19216' } }, // get special
{ x: 32, y: 9 },
{ x: 32, y: 10 },
{ x: 32, y: 11 }, // get 4
{ x: 32, y: 12 },
{ x: 32, y: 13 },
{ x: 32, y: 14 },
{ x: 32, y: 15 }, // get 8
{ x: 32, y: 16 } // get last
];
let newArr = arr.filter((e,i,ar) => (i%4 === 0 || e.monster || i===ar.length-1));
console.log(newArr);
Used .flatMap() to sort out all required objects. A custom function based on this answer counts how many keys each object had so that if an object had more than 2 keys it was considered special.The version in demo is streamlined into one line.
/** objSize(object)
Utility that returns a given Object's number of non-enumerated property keys it
has (String and Symbol).
*/
const objSize = obj => {
let strings = Object.getOwnPropertyNames(obj).length;
let symbols = Object.getOwnPropertySymbols(obj).length;
return strings + symbols;
}
Further details about .flatMap() are commented in demo.
let array = [
{ x: 31, y: 8 }, // get 1
{ x: 32, y: 8, z: { is: true, id: '19216' } },
{ x: 32, y: 9 },
{ x: 32, y: 10 },
{ x: 32, y: 11 }, // get 4
{ x: 32, y: 12 },
{ x: 32, y: 13 },
{ x: 32, y: 14 },
{ x: 32, y: 15 }, // get 8
{ x: 32, y: 16 } // get last
];
const objSize = obj => Object.getOwnPropertyNames(obj).length + Object.getOwnPropertySymbols(obj).length;
/*
.flatMap() is an array method that is basically a
combonation of `.map()` and `.flat()`. Here it is running
a function of 4 ternary controls:
1. if current index is 0:
(idx === 0)
return [xy]
2. if current index is a factor of 4:
(idx % 4 === 0)
return [xy]
3. if current xy has more than 2 keys:
(objSize(xy) > 2)
return [xy]
4. if current index is the last:
(idx === array.length - 1)
return [xy]
5. otherwise return []
each return is an array which is flattened when the final
array is returned. Therefore an empty array is a clean
removal which means no nulls, spaces, or empty values.
*/
let result = array.flatMap((xy, idx) => xy === 0 ? [xy] : idx % 4 === 0 ? [xy] : objSize(xy) > 2 ? [xy] : idx === array.length - 1 ? [xy] : []);
console.log(JSON.stringify(result));
Related
I am trying to fetch unique objects from an array which may have duplicate objects. I have tried new Set and new Map but i still haven't gotten my result.
For example i have the following array of objects
const myArray = [{ x: 10, y: 22}, { x: 11, y: 22}, { x: 12, y: 22}, { x: 12, y: 22}, { x: 12, y: 23}];
console.log([...new Set(myArray.map((item) => item.x && item.y ))]) // [22, 23]
when i want this
[{ x: 10, y: 22}, { x: 11, y: 22}, { x: 12, y: 22}, { x: 12, y: 23}];
it should remove the fourth object in myArray, since it is repeating
You can use reduce for that along with some:
const myArray = [{ x: 10, y: 22}, { x: 11, y: 22}, { x: 12, y: 22}, { x: 12, y: 22}, { x: 12, y: 23}];
const Filtered = [];
const filterDuplicates = myArray.reduce((arr, el) => {
if(!arr.some(current => current.x === el.x && current.y === el.y)) {
arr.push(el);
}
return arr;
}, Filtered);
console.log(Filtered);
What your map returns as follows
myArray.map((item) => item.x && item.y) // [ 22, 22, 22, 22, 23 ]
because it first check wheather the item.x is truthy or not. As it true always so it returns the value after &&
and when you apply the set, It will filter the unique value from the array
[...new Set(myArray.map((item) => item.x && item.y))] // [ 22, 23 ]
Alternate apprach
const myArray = [
{ x: 10, y: 22 },
{ x: 11, y: 22 },
{ x: 12, y: 22 },
{ x: 12, y: 22 },
{ x: 12, y: 23 },
];
const strArray = myArray.map(({ x, y }) => `${x}/${y}`);
const str = [...new Set(strArray)];
const result = str.map((str) => {
const [x, y] = str.split("/");
return { x, y };
});
console.log(result);
I have map called "marks" that has other maps as fields. I need to do something like a forEach loop (or a map) with this getting the key and the value of each nested map.
This is my data:
"marks" : {
"mark_01": {x: 10, y: 200},
"mark_02": {x: 200, y: 100},
...
"mark_99": {x: 1000, y: 1039}
}
What I am trying to do is:
// This is wrong but represents the main idea
const newMarks = marks.map(mark => {
// Get the mark key "mark_number"
// Change its value (x, y)
// Return an object with the same key and the new manipulated value
})
Any ideas? The resulted data has to look like this:
"marks" : {
"mark_01" : {x: 0, y: 190},
"mark_02" : {x: 190, y: 90},
...
"mark_99" : {x: 990, y: 1029}
}
Below snippet could help you
const { marks } = {
marks: {
mark_01: { x: 10, y: 200, other_if_have: 'a' },
mark_02: { x: 200, y: 100, other_if_have: 'b' },
mark_99: { x: 1000, y: 1039, other_if_have: 'c' },
},
}
const temp = Object.keys(marks).map((mark) => {
const manipulate = ({ x, y }) => ({
x: x - 10,
y: y - 10,
})
return [mark, { ...marks[mark], ...manipulate(marks[mark]) }]
})
const res = { marks: Object.fromEntries(temp) }
console.log(res)
Ref:
Object.keys() doc
Object.entries() doc
Another alternative solution:
const edited = Object.fromEntries(Object.entries(marks).map(
([k,{x,y}]) => [k,{x: x+10, y: y+10}]
));
You can check it here: https://jsfiddle.net/sandro_paganotti/ztw1exb4/13/
Could use a for...in loop:
const marks = {
"mark_01": {
x: 10,
y: 200
},
"mark_02": {
x: 200,
y: 100
},
"mark_99": {
x: 1000,
y: 1039
}
}
console.log("before", marks)
for (let i in marks) {
marks[i].x += 1;
marks[i].y += 1;
}
console.log("after", marks)
Though, you should note:
The problem with a for...in loop is that it iterates through properties in the Prototype chain. When you loop through an object with the for...in loop, you need to check if the property belongs to the object. You can do this with hasOwnProperty.
So to account for this:
const marks = {
"mark_01": {
x: 10,
y: 200
},
"mark_02": {
x: 200,
y: 100
},
"mark_99": {
x: 1000,
y: 1039
}
}
console.log("before", marks)
for (let i in marks) {
if (marks.hasOwnProperty(i)) {
marks[i].x += 1;
marks[i].y += 1;
}
}
console.log("after", marks)
This is a good article to check out for something like this.
if you have undetermined number of properties inside your object you can do nested for like that
let marks = {
"mark_01": {x: 10, y: 200, z: 300, ...},
"mark_02": {x: 200, y: 100, z: 10, ...},
"mark_99": {x: 1000, y: 1039, z: 1200, ...}
}
let newMarks = {}
for (const [key, value] of Object.entries(marks)) {
let newValues = {}
for (const [innerKey, innerValue] of Object.entries(value)) {
newValues[innerKey] = innerValue - 10
}
newMarks[key] = newValues
}
console.log(newMarks);
I have array of objects.
let coordinates = [
{ x: 8, y: 1 },
{ x: 8, y: 3 },
{ x: 6, y: 5 },
{ x: 4, y: 6 },
{ x: 3, y: 7 },
{ x: 6, y: 5 },
{ x: 3, y: 3 },
{ x: 1, y: 4 },
{ x: 3, y: 3 }
]
I stumbled upon this blog and this stackoverflow question but it only lets me find duplicates based on one property but I want to get the duplicate objects based on x and y properties like this:
[
{ x: 6, y: 5 },
{ x: 3, y: 3 }
]
We apply filter to check the uniquness of index, say if element is duplicate it will count the initial index and the current index will be filtered out.
var coordinates = [ { x: 8, y: 1 }, { x: 6, y: 5 }, { x: 4, y: 6 }, { x: 3, y: 7 }, { x: 6, y: 5 }, { x: 3, y: 3 }, { x: 1, y: 4 }, { x: 3, y: 3 }];
var result = coordinates.filter((val, i, self)=>self.findIndex(k=>k.x==val.x && k.y == val.y)!=i);
console.log(result)
UPDATE
coordinates = [ { x: 6, y: 5 }, { x: 6, y: 5 }, { x: 6, y: 5 },{ x: 4, y: 6 }, { x: 3, y: 7 }, { x: 6, y: 5 }, { x: 3, y: 3 }, { x: 3, y: 3 }, { x: 1, y: 4 }, { x: 3, y: 3 },{ x: 6, y: 5 }];
result = coordinates.reduce((acc, elem)=>{
key = Object.values(elem).join('|');
acc.unique[key] = acc.unique[key] || [];
acc.unique[key].length >0 ? acc.duplicate[key] = elem : acc.unique[key].push(elem);
return acc;
},{unique:{},duplicate:{}});
duplicate = Object.values(result.duplicate);
unique = Object.values(result.unique);
console.log(duplicate);
console.log(unique);
All the answers are not fully correct, because they don't apply to arrays with more than 2 duplicates of the same value, i.e.:
var coordinates = [
{ x: 8, y: 1 },
{ x: 8, y: 1 },
{ x: 8, y: 1 }
]
I used JSON.stringify() and Set structure to get unique duplicates from an array. And for the output I parsed strings back to the objects.
var coordinates = [
{ x: 8, y: 1 },
{ x: 6, y: 5 },
{ x: 4, y: 6 },
{ x: 3, y: 7 },
{ x: 6, y: 5 },
{ x: 3, y: 3 },
{ x: 1, y: 4 },
{ x: 3, y: 3 },
{ x: 3, y: 3 },
]
const duplicates = new Set();
const reducer = (set, val, index, arr) => arr.findIndex(k => k.x == val.x && k.y == val.y) != index ? set.add(JSON.stringify(val)) : set
coordinates.reduce(reducer, duplicates)
console.log(Array.from(duplicates).map(el => JSON.parse(el)))
You can use reduce and another array. Inside reduce callback use x & y to create an object key and check if that key exist in accumulator object. If it exist then push tyhe value to the dupArray
let coordinates = [
{ x: 8, y: 1 },
{ x: 8, y: 3 },
{ x: 6, y: 5 },
{ x: 4, y: 6 },
{ x: 3, y: 7 },
{ x: 6, y: 5 },
{ x: 3, y: 3 },
{ x: 1, y: 4 },
{ x: 3, y: 3 }
];
let dupArray = [];
let dups = coordinates.reduce((acc, curr) => {
const crtKey = '' + curr.x + '' + curr.y;
if (!acc[crtKey]) {
acc[crtKey] = 1;
} else {
dupArray.push(curr);
}
return acc;
}, {});
console.log(dupArray)
this filters only the duplicate array Objects by iterating all the Array items and for each item, iterating the Array again, using some to check if that specific item was anywhere in the array (arr) in a location before the current one (idx1) and also comparing the current item with the checked items in the some iterator by casting both to "string"
let coordinates = [
{ x: 8, y: 1 },
{ x: 8, y: 3 },
{ x: 6, y: 5 },
{ x: 4, y: 6 },
{ x: 3, y: 7 },
{ x: 6, y: 5 },
{ x: 3, y: 3 },
{ x: 1, y: 4 },
{ x: 3, y: 3 }
]
const dups = coordinates.filter(({x, y}, idx1, arr) =>
arr.some(({x:x2,y:y2}, idx2) => idx2 < idx1 && ""+x+y == ""+x2+y2 )
)
console.log( dups )
To make the check more robust, by allowing comparison of any key, what-so-ever JSON.stringify can be used to compare the arrays (assuming all array items are Objects:
const dups = coordinates.filter((item, idx1, arr) =>
arr.some((item2, idx2) => idx2 < idx1 && JSON.stringify(item) == JSON.stringify(item2))
I have two arrays of objects which contain a huge amount of data.
The structure of these two arrays goes something like this.
arr1 = [
{x: 1, y: '2018-01-01'},
{x: 2, y: '2018-01-02'},
{x: 3, y: '2018-01-03'},
{x: 5, y: '2018-01-05'},
....
]
arr2 = [
{x: 1, y: '2018-01-01'},
{x: 2, y: '2018-01-02'},
{x: 3, y: '2018-01-03'},
{x: 4, y: '2018-01-04'},
{x: 5, y: '2018-01-05'},
{x: 6, y: '2018-01-08'}
]
I want to update arr2 in such a way that it updates the array of objects with values that are only present in arr1 and drop any values not present in arr1. Note, I want to update the original arr2 and not return a new array.
I tried iterating through individual arrays and remove values not present but not luck.
You could get a map and iterate from the end for splicing unknown items or update changed values.
var arr1 = [{ x: 1, y: '2018-01-01x' }, { x: 2, y: '2018-01-02' }, { x: 3, y: '2018-01-03' }, { x: 5, y: '2018-01-05' }],
arr2 = [{ x: 1, y: '2018-01-01' }, { x: 2, y: '2018-01-02' }, { x: 3, y: '2018-01-03' }, { x: 4, y: '2018-01-04' }, { x: 5, y: '2018-01-05' }, { x: 6, y: '2018-01-08' }],
map = arr1.reduce((m, { x, y }) => m.set(x, y), new Map),
i = arr2.length;
while (i--) {
if (map.has(arr2[i].x)) {
if (map.get(arr2[i].x) !== arr2[i].y) {
arr2[i].y = map.get(arr2[i].x);
}
} else {
arr2.splice(i, 1);
}
}
console.log(arr2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have object array like this.
const array = [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 12 } ]
I want to count duplicates objects and store the count as new object field.
I found this snippet and it work great but it not exactly what i need.
const names = [{ _id: 1 }, { _id: 1}, { _id: 2}, { _id: 1}]
const result = [...names.reduce( (mp, o) => {
if (!mp.has(o._id)) mp.set(o._id, Object.assign({ count: 0 }, o));
mp.get(o._id).count++;
return mp;
}, new Map).values()];
console.log(result);
It works with object with one field _id. In my case there are two, x and y
How should I modify that code?
In brief...I would like to receive the result:
result = [ { x: 1, y: 2, count:3 }, { x: 3, y: 4, count:2 }, { x: 3, y: 12, count:1 } ]
You can use Object.values() and reduce() methods to return new array of objects.
const array = [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 12 } ]
const result = Object.values(array.reduce((r, e) => {
let k = `${e.x}|${e.y}`;
if(!r[k]) r[k] = {...e, count: 1}
else r[k].count += 1;
return r;
}, {}))
console.log(result)
Here is the solution with Map and spread syntax ...
const array = [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 12 } ]
const result = [...array.reduce((r, e) => {
let k = `${e.x}|${e.y}`;
if(!r.has(k)) r.set(k, {...e, count: 1})
else r.get(k).count++
return r;
}, new Map).values()]
console.log(result)
One way to do it would be to create an index mapping both x and y to the result entry:
let index = { };
let result = [ ];
const array = [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 12 } ];
array.forEach(point => {
let key = '' + point.x + '||' + point.y;
if (key in index) {
index[key].count++;
} else {
let newEntry = { x: point.x, y: point.y, count: 1 };
index[key] = newEntry;
result.push(newEntry);
}
});
console.log(result);