d3 filter duplicates names from csv - javascript

with d3.csv I am reading a csv that is then stored such that
console.log(data[0]) returns
Object {username: "mark", y: 0, x: 0, value: 0}
Now I want to extract from data only the first occurence of each username.
In python pandas I would have used data.drop_duplicates(columns='username')
EDIT:
Consider the following example:
var X = [{username: "a", y: 0, x: 0, value: 0},
{username: "b", y: 0, x: 0, value: 0},
{username: "a", y: 1, x: 0, value: 0}
{username: "c", y: 0, x: 0, value: 0}
{username: "b", y: 1, x: 0, value: 0}]
it should return only:
var Y = [{username: "a", y: 0, x: 0, value: 0},
{username: "b", y: 0, x: 0, value: 0},
{username: "c", y: 0, x: 0, value: 0}
i.e. f(X) = Y

Since you want to find the first occurrence of each username, this question has nothing to do with D3, and can be solved with plain JavaScript.
There are several ways for doing this. A simple one is using Array.prototype.find():
The find() method returns the value of the first element in the array that satisfies the provided testing function. (emphasis mine)
Here is a demo. First, we get the unique values for usernames as an array:
var users = [...new Set(data.map(function(d) {
return d.username
}))];
Then, we use a map with filter to get the first occurences:
var firstOccurrence = users.map(function(d) {
return data.find(function(e) {
return e.username === d
})
});
Check the demo:
var data = [{
username: "a",
y: 0,
x: 0,
value: 0
}, {
username: "b",
y: 0,
x: 0,
value: 0
}, {
username: "a",
y: 1,
x: 0,
value: 0
}, {
username: "c",
y: 0,
x: 0,
value: 0
}, {
username: "b",
y: 1,
x: 0,
value: 0
}];
var users = [...new Set(data.map(function(d) {
return d.username
}))]
var firstOccurrence = users.map(function(d) {
return data.find(function(e) {
return e.username === d
})
});
console.log(firstOccurrence)

You can employ a Map to keep track of what usernames have already been processed. Using the username as the key ensures the uniqueness of this value. To get the first occurence of this value instead of the last one, you further have to use Map.prototype.has() to check if each particular value has already been added.
var X = [
{username: "a", y: 0, x: 0, value: 0},
{username: "b", y: 0, x: 0, value: 0},
{username: "a", y: 1, x: 0, value: 0},
{username: "c", y: 0, x: 0, value: 0},
{username: "b", y: 1, x: 0, value: 0}
];
var uniqX = new Map(); // Construct map of unique usernames
X.forEach(x => {
if (!uniqX.has(x.username)) uniqX.set(x.username, x); // Only add, if not present
});
var Y = uniqX.values(); // Retrieve the result set
console.log(...Y);
Or, rewritten for the sake of mathematical aesthetics:
var X = [
{username: "a", y: 0, x: 0, value: 0},
{username: "b", y: 0, x: 0, value: 0},
{username: "a", y: 1, x: 0, value: 0},
{username: "c", y: 0, x: 0, value: 0},
{username: "b", y: 1, x: 0, value: 0}
];
function f(_) {
let uniqX = new Set();
return _.filter(({username}) => !uniqX.has(username) && uniqX.add(username));
}
var Y = f(X);
console.log(...Y);

Related

Foreach Array Element Search for values in multidimension Array javascript

Trying to match each element of an array to a set of coordinates in a multidimensions array in the following manner:
array1= [0, 5, 4]
array2 = [
{x: 1, y: 4, name: 'A', w: 0},
{x: 2, y: 8, name: 'E', w: 4},
{x: 3, y: 1, name: 'F', w: 5}];
I am hoping to match each element of array 1 to the value of w in array 2
0 -> {x: 1, y: 4, name: 'A', w: 0}
5 -> {x: 3, y: 1, name: 'F', w: 5}
4 -> {x: 2, y: 8, name: 'E', w: 4}
I want to return :
[
{x:1, y,4}, {x:3, y:1},
{x:3, y:1}, {x:2, y:8},
...
];
You should return the required x and y coordinates as an array.
Please find a working fiddle for that.
const array1 = [0, 5, 4]
const array2 = [
{ x: 1, y: 4, name: 'A', w: 0 },
{ x: 2, y: 8, name: 'E', w: 4 },
{ x: 3, y: 1, name: 'F', w: 5 },
];
const tempArray = array1.map((item) => array2.find((node) => node.w === item));
// console.log(tempArray);
const finalArray = tempArray.map((currentNode, index, actualArray) => {
const nextIndex = index === actualArray.length - 1 ? 0 : index + 1;
return [
{ x: currentNode.x, y: currentNode.y },
{ x: actualArray[nextIndex].x, y: actualArray[nextIndex].y },
];
});
console.log(finalArray);
You can chain array filter and map to do something like this
Array.prototype.filter() to filter all elements based on the index from first array and
Array.prototype.map() to modify the object so as to only show the x and y co-ordinates
const array1 = [0, 5, 4];
const array2 = [
{ x: 1, y: 4, name: "A", index: 0 },
{ x: 2, y: 8, name: "E", index: 4 },
{ x: 3, y: 1, name: "F", index: 5 },
];
const newArr = array2.filter(x => array1.includes(x.index)).map(x => ({ x: x.x, y: x.y }));
console.log(newArr)

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);

Normalize Data in array of Objects (JavaScript)

im trying to normalize some data sitting in an array of objects.
[
{id: 1, number: 10, x: 0.3, y: 0.4, …}
{id: 2, number: 5, x: 0.5, y: 0.2, …}
{...}
{...}
{...}
]
I want to map the x and y entry's on a new value between 0 - 1250. So I get the following Array of Objects
[
{id: 1, number: 10, x: 375, y: 500, …}
{id: 2, number: 5, x: 625, y: 250, …}
{...}
{...}
{...}
]
Whats the best Practice for that?
Best,
Chris
You can use Array.map
const arr = [
{id: 1, number: 10, x: 0.3, y: 0.4},
{id: 2, number: 5, x: 0.5, y: 0.2}
];
// Use Array.map to iterate
const arr1 = arr.map(ob => {
ob.x*=1250;
ob.y*=1250;
return ob;
});
console.log(arr1);
Some thing like this with map method.
const arr = [
{id: 1, number: 10, x: 0.3, y: 0.4},
{id: 2, number: 5, x: 0.5, y: 0.2},
];
const res = arr.map(({x, y, ...rest}) => ({...rest, x: x * 1250, y: y * 1250 }));
console.log(res)
Assuming arr is your array of object. You can use map which returns new modified array.
let arr = [
{
id: 1, number: 10, x: 0.3, y: 0.4,
},
{
id: 2, number: 5, x: 0.5, y: 0.2
}
];
const normalize = (obj) => {
x = obj.x * 1250;
y = obj.y * 1250;
return {...obj, x, y};
// If you're only using mutating then
// above lines can be
// obj.x *= 1250;
// obj.y *= 1250;
// return obj;
}
// Not mutating array, output new array
const nonMutating = (arr) => {
let newRes = [];
arr.forEach(a => {
newRes.push(normalize(a));
});
return newRes;
}
console.log(nonMutating(arr));
console.log("\n");
// Mutating input array
const mutating = (arr) => {
return arr.map(a => normalize(a));
}
console.log(mutating(arr));

Merge objects in array with similar key

I have an array of objects:
objArray = [
{x: 1, y: 7},
{x: 2, y: 14},
{x: 1, z: 9},
{x: 2, z: 18}
{x: 1, n: 6}
{x: 2, n: 16}
]
Is there an efficient way to merge for "X" without a for loop? so that I end up with:
objArray = [
{x: 1, y: 7, z: 9, n: 6},
{x: 2, y: 14, z: 18, n: 16}
]
So look for common objArray[n]["x"] and merge all hits into one object? It's OK to modify the original array or create a new one.
I'm aware this can be done with a loop, but I'm trying to avoid too many loops for this implementation, though I'm not sure if a reduce or a filter would work for this.
You could take a Map and group by property x.
var array = [{ x: 1, y: 7 }, { x: 2, y: 14 }, { x: 1, z: 9 }, { x: 2, z: 18 }, { x: 1, n: 6 }, { x: 2, n: 16 }],
result = Array.from(
array
.reduce((m, o) => m.set(o.x, Object.assign({}, m.get(o.x), o)), new Map)
.values()
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use reduce method to build an object and then Object.values to get an array.
const data = [{"x":1,"y":7},{"x":2,"y":14},{"x":1,"z":9},{"x":2,"z":18},{"x":1,"n":6},{"x":2,"n":16}]
const res = data.reduce((r, {x, ...rest}) => {
if(!r[x]) r[x] = {x, ...rest}
else Object.assign(r[x], rest);
return r;
}, {})
const result = Object.values(res);
console.log(result)
You can do it with Array#reduce:
const objArray = [
{x: 1, y: 7},
{x: 2, y: 14},
{x: 1, z: 9},
{x: 2, z: 18},
{x: 1, n: 6},
{x: 2, n: 16},
]
const result = Object.values( objArray.reduce(
(p,c) => (p[c.x] = Object.assign( {}, p[c.x], c ), p ), {}
) );
console.log( result );

Join array of objects

I have asked this question before, but it seems the solution doesn't work well.
I have two objects:
var a = [{ x: 0, y: 0, color: "green", value: undefined, weight: 1 }, { x: 0, y: 1, color: "red", value: undefined, weight: 1 }];
var b = [{ x: 0, y: 0, value: 1}, { x: 0, y: 1, value: 3}];
I want to join them into a single object, like this:
var c = [{ x: 0, y: 0, color: "green", value: 1, weight: 1 }, { x: 0, y: 1, color: "red", value: 3, weight: 1 }];
Note: array A will always have 25 entries, while array b not.
The proposed solution was:
var extendedArray = $.extend({}, a, b);
However this makes an array of two entries, where not all values are preserved.
I have also tried the following functions:
var e = $.merge(a, b);
var output = a.concat(b);
function jsonConcat(o1, o2) {
for (var key in o2) {
o1[key] = o2[key];
}
return o1;
}
var c = {};
c = jsonConcat(c, a);
c = jsonConcat(c, b);
Any help or push in the right direction would be very much appreciated!
Edited to reflect that b can be smaller but never larger than a.
function merge(a,b){
//Don't want to mutate a
var result = a.slice();
for(var i = 0; i < b.length; i++){
for (var attrname in b[i]) {
result[i][attrname] = b[i][attrname];
}
}
return result;
}
Code partly taken from accepted answer in: How can I merge properties of two JavaScript objects dynamically?
Assuming that the two arrays have the same length, an in place merge could be something like this:
var a = [{ x: 0, y: 0, color: "green", value: undefined, weight: 1 }, { x: 0, y: 1, color: "red", value: undefined, weight: 1 }];
var b = [{ x: 0, y: 0, value: 1, weight: 1 }, { x: 0, y: 1, value: 3, weight: 1 }];
function merge_objects(o1, o2) {
Object.keys(o2).forEach(
function(key) {
o1[key] = o2[key];
});
}
function merge(a, b) {
if (a.length != b.length) {
throw new Error();
}
for (var ix=0; ix<a.length; ix++) {
merge_objects(a[ix], b[ix]);
}
}
$.extend without the first argument set to true will only merge the "first level" of your objects. It works if your properties are only strings and numbers, but if some properties are objects, it can result in unexpected behaviour.
I think you're looking for $.extend(true,obj1,obj2) .
example
var a = [{ x: 0, y: 0, color: "green", value: undefined, weight: 1 }, { x: 0, y: 1, color: "red", value: undefined, weight: 1 }];
var b = [{ x: 0, y: 0, value: 1, weight: 1 }, { x: 0, y: 1, value: 3, weight: 1 }];
var c = $.extend(true,[],a,b);
console.log(c instanceof Array); // true
see the doc for details
the first argument true tells the method to perform a "deep" copy, recursively.

Categories