Map object at same start value - javascript

I have an array of object like that
const object = [
{name: 'One', data: [{x:1648741414000 , y:50},{x:1648741475000, y:60}, {x:1648741536000, y:70}]},
{name: 'Two', data: [{x:1648741403000, y:50},{x:1648741414000 , y:60}, {x:1648741525000, y:70}]},{x:1648741476000, y:80}
{name: 'Three', data: [{x:1648741422000, y:50},{x:1648741414000 , y:60},{x:1648741475000, y:70}, {x:1648741536000, y:90}]}
]
I need to map my array to have only x data values that match the others object
const result= [
{name: 'One', data: [{x:1648741414000 , y:50},{x:1648741475000, y:60},{x:1648741536000, y:70}]},
{name: 'Two', data: [{x:1648741414000 , y:60}, {x:1648741535000, y:70},{x:1648741476000, y:80}]},
{name: 'Three', data: [{x:1648741414000 , y:60},{x:1648741475000, y:70}, {x:1648741536000, y:90}]}
]
Thanks for your help

You could get common x values with their indices and slice the arrays with the found index.
const
array = [{ name: 'One', data: [{ x: 1648741414000, y: 50 }, { x: 1648741475000, y: 60 }, { x: 1648741536000, y: 70 }] }, { name: 'Two', data: [{ x: 1648741403000, y: 50 }, { x: 1648741414000, y: 60 }, { x: 1648741525000, y: 70 }, { x: 1648741476000, y: 80 }] }, { name: 'Three', data: [{ x: 1648741422000, y: 50 }, { x: 1648741414000, y: 60 }, { x: 1648741475000, y: 70 }, { x: 1648741536000, y: 900 }] }],
indices = Object
.values(array.reduce((r, { data }, _, { length }) => {
data.some((o, i) => (r[o.x] ??= []).push(i) === length);
return r;
}, {}))
.find(({ length }) => length === array.length),
result = array.map(({ name, data }, i) =>
({ name, data: data.slice(indices[i]) })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A bit longer code.
const
array = [{ name: 'One', data: [{ x: 1648741414000, y: 50 }, { x: 1648741475000, y: 60 }, { x: 1648741536000, y: 70 }] }, { name: 'Two', data: [{ x: 1648741403000, y: 50 }, { x: 1648741414000, y: 60 }, { x: 1648741525000, y: 70 }, { x: 1648741476000, y: 80 }] }, { name: 'Three', data: [{ x: 1648741422000, y: 50 }, { x: 1648741414000, y: 60 }, { x: 1648741475000, y: 70 }, { x: 1648741536000, y: 900 }] }],
length = array.length,
hashes = {},
result = [];
let indices;
outer: for (const { data } of array) {
for (let i = 0; i < data.length; i++) {
const { x } = data[i];
if (!hashes[x]) hashes[x] = [];
hashes[x].push(i);
if (hashes[x].length === length) {
indices = hashes[x];
break outer;
}
}
}
for (let i = 0; i < length; i++) {
const { name, data } = array[i];
result.push({ name, data: data.slice(indices[i]) });
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How to store separately the repeated values inside array of objects

I have this array of objects in Javascript:
var arrayData = [
{ id: "0", x: -1, y: 38},
{ id: "1", x: -2.7823, y: 43.444},
{ id: "2", x: -1.1654, y: 38.12088},
{ id: "3", x: -1, y: 38},
{ id: "4", x: -2.7823, y: 43.444 },
{ id: "5", x: -1.1654, y: 38.12088},
{ id: "6", x: -1.1654, y: 38.12088},
]
and i'd need to check what objects have the same property x and y and someway store this objects separately so i can access later to them.
i've got this:
var copy = arrayData.slice(0);
// first loop goes over every element
for (var i = 0; i < arrayData.length; i ++) {
// loop over every element in the copy and see if it's the same
for (var w = i + 1; w < copy.length; w++) {
if ((arrayFotos[i].x === copy[w].x) && (arrayFotos[i].y === copy[w].y)) {
if(!duplicates.includes(arrayFotos[i].id))
duplicates.push(arrayFotos[i].id);
if(!duplicates.includes(copy[w].id))
duplicates.push(copy[w].id);
}
}
This returns me an array with all ids of the objects which
have repeated x and y properties but i need a way to store
them separately so i have: [0,3][1,4][2,5,6]. Is there any
way to do it? Thanks if u want to help me.
You could take a hash table and collect id for same coordinates.
var arrayData = [{ id: "0", x: -1, y: 38 }, { id: "1", x: -2.7823, y: 43.444 }, { id: "2", x: -1.1654, y: 38.12088 }, { id: "3", x: -1, y: 38 }, { id: "4", x: -2.7823, y: 43.444 }, { id: "5", x: -1.1654, y: 38.12088 }, { id: "6", x: -1.1654, y: 38.12088 }],
duplicates
hashtable = {};
for (var i = 0; i < arrayData.length; i++) {
let key = ['x', 'y'].map(k => arrayData[i][k]).join('|');
hashtable[key] = hashtable[key] || [];
hashtable[key].push(arrayData[i].id);
}
duplicates = Object.values(hashtable);
console.log(duplicates);

How to compare two arrays with objects to filter the ones that are similar (vanilla JS)

Problem
Given the following two arrays:
const myArr1 = [
{ locationPath: 'R0', locationOnGrid: { x: '0', y: '0' } }, // same as second in myArr2
{ locationPath: 'U5', locationOnGrid: { x: '1', y: '0' } },
{ locationPath: 'L3', locationOnGrid: { x: '3', y: '7' } } // same as second in myArr2
]
const myArr2 = [
{ locationPath: 'D0', locationOnGrid: { x: '0', y: '0' } }, // same as second in myArr1
{ locationPath: 'L5', locationOnGrid: { x: '3', y: '7' } }, // same as third in myArr1
{ locationPath: 'L7', locationOnGrid: { x: '0', y: '1' } },
{ locationPath: 'R2', locationOnGrid: { x: '2', y: '2' } }
]
// Do something to 'filter' out the objects that are similar.
Result
Then what I would like to also be able to do after the comparison has been made, is to filter out a certain result:
// Result of the 'filter' function
const found = [
{ locationPath: 'R0', locationOnGrid: { x: '0', y: '0' } },
{ locationPath: 'L5', locationOnGrid: { x: '3', y: '7' } }
];
// and want to do something like the following on the found array:
const startingPoint = ({locationOnGrid}) => {
return JSON.stringify(locationOnGrid) !== JSON.stringify({ x: '0', y: '0' });
};
const filteredFound = found.filter(startingPoint);
console.log(filteredFound);
// output:
// Array(1)
// 0: {locationPath: "L5", locationOnGrid: {…}}
What I've tried so far:
There are some Stackoverflow questions that relate to the comparison between two Arrays. For example the question Simplest code for array intersection and How to filter array by comparing two elements comes really close.
// does not work
const found = myArr2.filter((item, index) => {
return
(
item.locationOnGrid.x === myArr1[index].locationOnGrid.x &&
item.locationOnGrid.y === myArr1[index].locationOnGrid.y
);
});
console.log(found);
Also two arrays don't always necessarily have the same amount of objects. In the example myArr2 has 1 more object. So this works. But for another scenario it could very well be that myArr1 has more objects than myArr2.
You could get a Set with normalised key/value pairs, ordered by the key and create a JSON.
Then filter the first array and get the common locationOnGrid objects.
const
stringified = o => JSON.stringify(Object.entries(o).sort((a, b) => a[0].localeCompare(b[0]))),
array1 = [{ locationPath: 'R0', locationOnGrid: { x: '0', y: '0' } }, { locationPath: 'U5', locationOnGrid: { x: '1', y: '0' } }, { locationPath: 'L3', locationOnGrid: { x: '3', y: '7' } }],
array2 = [{ locationPath: 'D0', locationOnGrid: { x: '0', y: '0' } }, { locationPath: 'L5', locationOnGrid: { x: '3', y: '7' } }, { locationPath: 'L7', locationOnGrid: { x: '0', y: '1' } }, { locationPath: 'R2', locationOnGrid: { x: '2', y: '2' } }],
set2 = new Set(array2.map(({ locationOnGrid }) => stringified(locationOnGrid))),
common = array1.filter(({ locationOnGrid }) => set2.has(stringified(locationOnGrid)));
console.log(common);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Summarize the frequency of array of objects

Assume I have the following array of objects.
data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
]
what I need is to summarize the frequency of identical object in the array. The output will look like:
summary = [
{ x: 1, y: 1, f: 3 },
{ x: 1, y: 2, f: 1 },
{ x: 2, y: 2, f: 2 },
{ x: 3, y: 3, f: 1 }
]
For now I have this code
const summary = data.map((item, index, array) => {
return { x: item.x, y: item.y, f: array.filter(i => i === item).length };
});
But I suppose I can do better by using reduce or includes. Any ideas?
Reduce into an object whose keys uniquely represent an object, whose values are the object (with x, y, and f properties). On each iteration, increment the appropriate key's f property, or create the key on the accumulator if it doesn't exist yet:
const data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
];
const countObj = data.reduce((a, obj) => {
const objString = obj.x + '_' + obj.y;
if (!a[objString]) {
a[objString] = { ...obj, f: 1 };
} else {
a[objString].f++;
}
return a;
}, {});
const output = Object.values(countObj);
console.log(output);
Don't use map - you're better off using reduce like so:
const summary = Object.values(data.reduce((a, { x, y }) => {
a[`${x}-${y}`] = a[`${x}-${y}`] || { x, y, f: 0 };
a[`${x}-${y}`].f++;
return a;
}, {}));
Object.values(data.reduce((sum, i) => {
i_str = JSON.stringify(i); // objects can't be keys
sum[i_str] = Object.assign({}, i, {f: sum[i_str] ? sum[i_str].f+1 : 1});
return sum;
}, {}));
Note:
This snippet will work on an array of any arbitrary objects, as long as they are stringifiable.
Results are not ordered, since object keys aren’t ordered. If this is an issue, sort at will.
What you’re doing, is counting the times an object exists in an array. You probably want results external to the objects, as opposed to embedded in them. Something along these lines might be more manageable, returning a mapping of descriptions of the objects to a count:
data.reduce((sum, i) => {
i_str = JSON.stringify(i); // objects can't be keys
sum[i_str] = sum[i_str] ? sum[i_str]+1 : 1;
return sum;
}, {});
A simple solution based on Array#reduce would be as detailed below:
const data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
];
const summary = data.reduce((frequencySummary, item) => {
/* Find a match for current item in current list of frequency summaries */
const itemMatch = frequencySummary.find(i => i.x === item.x && i.y === item.y)
if(!itemMatch) {
/* If no match found, add a new item with inital frequency of 1 to the result */
frequencySummary.push({ ...item, f : 1 });
}
else {
/* If match found, increment the frequency count of that match */
itemMatch.f ++;
}
return frequencySummary;
}, []);
console.log(summary)
I know using reduce is probably better, but I tend to use forEach and findIndex for better readability.
var data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
];
var summary = [];
data.forEach(function(d){
var idx = summary.findIndex(function(i){
return i.x === d.x && i.y === d.y;
});
if(idx < 0){
var sum = Object.assign({}, d);
sum.f = 1;
summary.push(sum);
} else {
summary[idx].f = summary[idx].f + 1;
}
});
console.log(summary);
Create nested objects. The outer object uses x values as keys, the nested object contains y values as keys, and the values are the frequencies.
data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
];
const nested = data.reduce((a, {x, y}) => {
a[x] = a[x] || {};
a[x][y] = a[x][y] ? a[x][y] + 1 : 1
return a;
}, {});
const summary = [];
Object.keys(nested).forEach(x => Object.keys(nested[x]).forEach(y => summary.push({x, y, f: nested[x][y]})));
console.log(summary);
You can use reduce and Map, club the use x and y as key, on every iteration check if the same key is already present on Map than just increase f count by 1 if not than set it to 1
const data = [{ x: 1, y: 1 },{ x: 2, y: 2 },{ x: 3, y: 3 },{ x: 2, y: 2 },{ x: 1, y: 1 },{ x: 1, y: 2 },{ x: 1, y: 1 }];
const countObj = data.reduce((a, obj) => {
const objString = obj.x + '_' + obj.y;
let value = a.get(objString) || obj
let f = value && value.f || 0
a.set(objString, { ...value, f: f+1 })
return a;
}, new Map());
console.log([...countObj.values()]);

summarize values of objects with same attribute name

I have an array filled with objects. The following example shows the structure of the objects.
let array = [
{
data: [{name:'a', value:20}, {name:'b', value:10}, {name:'c', value:5}]
},
{
data: [{name:'d', value:20}, {name:'a', value:10}, {name:'e', value:40}]
},
{
data: [{name:'b', value:30}, {name:'a', value:5}]
}
];
I'm trying to iterate through all the data values and summarize all the identical letters and sum up there values in a new array. So the new array should look like this:
let array = [{name:'a', value:35}, {name:'b', value:40}, {name:'c', value:5}, {name:'d', value:20}, {name:'e', value:40}];
This is my current approach but I don't get it to work.
let prevData = '';
let summarizedArray = [];
for(let i = 0; i < array.length; i++) {
for(let j = 0; j < array[i].data.length; j++) {
if(prevData === array[i].data[j].name) {
let summarized = {
name: array[i].data[j].name;
value: prevData.value + array[i].data[j].value;
}
summarizedArray.push(summarized);
}
prevData = array[i].data[j];
}
}
// Edited Example:
let array = [
{
data: [{name:'a', value1:20, value2:90, value3:'foo'},
{name:'b', value1:30, value2:20, value3:'boo'}]
},
data: [{name:'c', value1:5, value2:10, value3:'goo'},
{name:'a', value1:30, value2:20, value3:'foo'}]
},
{
];
The values should be bundled by same names. The values of Value1 and Value2 should be added up and Value3 is always the same for each name.
So the result should look like this:
let result = [{name:'a', value1:50, value2:110, value3:'foo'},
{name:'b', value1:30, value2:20, value3:'boo'},
{name:'c', value1:5, value2:10, value3:'goo'}
];
You could take a Map and collect all values. Later get an array of object of the collected values.
let array = [{ data: [{ name: 'a', value: 20 }, { name: 'b', value: 10 }, { name: 'c', value: 5 }] }, { data: [{ name: 'd', value: 20 }, { name: 'a', value: 10 }, { name: 'd', value: 40 }] }, { data: [{ name: 'b', value: 30 }, { name: 'a', value: 5 }] }],
result = Array.from(
array.reduce(
(m, { data }) => data.reduce(
(n, { name, value }) => n.set(name, (n.get(name) || 0) + value),
m
),
new Map
),
([name, value]) => ({ name, value })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
For a more convoluted object, you could take single properties to add, after a check for the type.
var array = [{ data: [{ name: 'a', value1: 20, value2: 90, value3: 'foo' }, { name: 'b', value1: 30, value2: 20, value3: 'boo' }] }, { data: [{ name: 'c', value1: 5, value2: 10, value3: 'goo' }, { name: 'a', value1: 30, value2: 20, value3: 'foo' }] }],
result = Array.from(
array.reduce(
(m, { data }) => {
data.forEach(o => {
var temp = m.get(o.name);
if (!temp) {
m.set(o.name, temp = {});
}
Object.entries(o).forEach(([k, v]) => {
if (k === 'name') return;
if (typeof v === 'number') {
temp[k] = (temp[k] || 0) + v;
} else {
temp[k] = v;
}
});
});
return m;
},
new Map
),
([name, value]) => Object.assign({ name }, value)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Selecting a 'property value' from an array based on another 'property value' in javascript

I have an array like this.
var nodes = [{ID:"101", x:100, y:200}
,{ID:"102", x:200, y:200}
,{ID:"103", x:300, y:300}
,{ID:"104", x:200, y:300}];
I'd like to have a function which takes node's ID as input and return its (x,y).
For example, the function coordinates(103)should read the array (nodes) and return x = 300, y = 300 when it's called. Any pointer is appreciated. Thanks :)
This is what I have so far. It works but I'd like to know neater and tidier methods.
function coordinates(id){
for (var i=0 in nodes){
if(nodes[i].ID == id){
return { x: nodes[i].x, y: nodes[i].y};
}
}
}
console.log(coordinates(102));
basically you're looking at something like this
var f = function(id){
var match = nodes.filter(function(d){
return d.ID === id;
})
return match && match.length && {x: match[0].x, y:match[0].y}
|| {x: undefined, y: undefined};
};
then f('101') outputs {x: 100, y:200} and if cannot find a match then it will output {x: undefined, y: undefined}
You can use .filter, like so
var nodes = [{
ID: "101",
x: 100,
y: 200
}, {
ID: "102",
x: 200,
y: 200
}, {
ID: "103",
x: 300,
y: 300
}, {
ID: "104",
x: 200,
y: 300
}];
function coordinates(nodes, id) {
var result = nodes.filter(function (el) {
return +el.ID === id;
});
if (result && result.length) {
result = result[0];
return {
x: result.x,
y: result.y
};
}
return null;
}
console.log(coordinates(nodes, 103));
See comments inline:
Demo
var nodes = [{
ID: "101",
x: 100,
y: 200
}, {
ID: "102",
x: 200,
y: 200
}, {
ID: "103",
x: 300,
y: 300
}, {
ID: "104",
x: 200,
y: 300
}];
var noOfCord = nodes.length;
var coordinates = function(id) {
for (var i = 0; i < noOfCord; i++) {
if (nodes[i].ID == id) {
return {
x: nodes[i].x,
y: nodes[i].y
};
}
}
}
document.write(coordinates(103).x + ', ' + coordinates(103).y);
Using array filter, Try:
function coordinates(id){
return nodes.filter(function(e){ return e.ID == id })[0]
}
var nodes=[{ID:"101",x:100,y:200},{ID:"102",x:200,y:200},{ID:"103",x:300,y:300},{ID:"104",x:200,y:300}];
var result = coordinates("103");
document.write("<pre>" + JSON.stringify(result, null, 3));
Brilliant solutions with concrete JavaScript have already been proposed by people here. So I propose another alternative using underscore.js, just in case you're curious.
function coordinates(id){
var n = _.findWhere(nodes, {ID: id});
return {x: n.x, y: n.y }
}

Categories