JS React: Two Objects, change properties if name matches - javascript

i have a problem with Objects in JavaScript (React).
I have two different Objects, which are generated from two different XML-Files. Each Object has the same Names in it but the points and position can be different. My Goal is to add the Points from the second Object to the First if the name matches.
The Structure of the Objects is the following:
let obj = [{name: "Max", points: 2},{name:"Marc", points: 1}]
let obj2 = [{name:"Marc", points: 2},{name: "Max", points: 1}]
The Goal is to have one updated Object:
let updatedObj = [{name: "Max", points:3},{name:"Marc", points:3}]
My Code looks like this rn:
import React, {useState} from 'react'
import axios from 'axios'
const App = () => {
const [list1,setList1] = useState()
const [list2,setList2] = useState()
const readFile = (file, number) => {
const dayPath = `/files/`;
axios.post(`http://localhost:5001/getData`, {dayPath, file})
.then(res => {
// File Number 1
if(number === 1){
let obj = res.data.vehicles[1].vehicle.map((item) => (
{
name: item.name,
points: res.data.vehicles[0] - Number(item.Position) +1 // Vehicles Total - Position + 1
})
)
setList(obj)
}else {
/*
* Get second Object -> Map -> Find matching Name ->
* Keep the Name Value -> (Calculate) and Add Points ->
* Push to State
*/
}
})}
}
return (
<div>App</div>
)
export default App
I've tried it with Object.entries, but only the last item was updated and the others were empty.
Thanks for your help!

let obj = [{name: "Max", points: 2},{name:"Marc", points: 1}]
let obj2 = [{name:"Marc", points: 2},{name: "Max", points: 1}]
const updatedObj = obj.map((person)=> {
const personRes = obj2.find((searchPerson)=> searchPerson.name === person.name)
if(personRes){
person.point += personRes.point
}
return person
})

You could use this createCombinedArray function. It is a little longer than some solutions, but the function is very easy to read, flexible (even with inconsistent or incorrect inputs), and has a runtime complexity of O(n) (A.K.A. a linear runtime complexity):
let obj = [{name: "Max", points: 2},{name:"Marc", points: 1}]
let obj2 = [{name:"Marc", points: 2},{name: "Max", points: 1}]
function createCombinedArray(arr1, arr2) {
//we'll keep track of how many points are associated with a name
let pointTracker = {}
//total all the points for every name in arr1
for (let i = 0; i < arr1.length; i++) {
let currObj = arr1[i]
//if this object has a 'name' property and a property 'number' that
//is actually a number
if (currObj.hasOwnProperty('name') && !isNaN(currObj.points)) {
//then add it to the name's total if it exists
if (pointTracker[currObj.name] !== undefined) {
pointTracker[currObj.name] += +currObj.points
} else {
//or if it doesn't exist, then make a new entry in your point
//tracker system with those points
pointTracker[currObj.name] = +currObj.points
}
}
}
//in addtion, total all the points for every name in arr2, also
//in the pointTracker object
for (let i = 0; i < arr2.length; i++) {
let currObj = arr2[i]
//if this object has a 'name' property and a property 'number' that
//is actually a number
if (currObj.hasOwnProperty('name') && !isNaN(currObj.points)) {
//then add it to the name's total if it exists
if (pointTracker[currObj.name] !== undefined) {
pointTracker[currObj.name] += +currObj.points
} else {
//or if it doesn't exist, then make a new entry in your point
//tracker system with those points
pointTracker[currObj.name] = +currObj.points
}
}
}
let combinedArr = []
//now fill in the currently-empty return array, by making and
//pushing objects for all the names found in our pointTracker object
for (let name in pointTracker) {
combinedArr.push({name: name, points: pointTracker[name]})
}
return combinedArr
}
//here's the function in action
const newObj = createCombinedArray(obj, obj2)
console.log(newObj)
This code will print out the array
[ { name: 'Max', points: 3 }, { name: 'Marc', points: 3 } ]
Just include the function code anywhere in the same .js file in your React project, and use the function where ever you like in that file.
One possible downside to this solution is that if the objects in your original arrays contained any properties that weren't name or points, then those properties' values won't be inside of the new object.

Related

What's a better algorithm to remove duplicates from an array of objects?

I have an array of objects that looks something like (rough example):
[{id:1, stuff:moreStuff}, {id:6, manyStuff,Stuffing}, {id:4, yayStuff, stuff}, {id:6, manyStuff, Stuffing}]
The problem is that in the array, there are several duplicate objects. The current solution I've thought of so far is something along the lines of this:
const DuplicateCheck = []
const FinalResult = []
for (let i = 0; i < ArrayOfObjects.length; i++) {
let isPresent = false;
for (let j = 0; j < duplicateCheck.length; j++) {
if (ArrayOfObjects[i].id == duplicateCheck[j]) {
isPresent = true;
}
}
if (isPresent = false) {
DuplicateCheck.push(ArrayOfObjects[i].id
FinalResult.push(ArrayOfObjects[i]
}
}
Now after learning big O, it seems like this is a very inefficient way to go about doing this problem. So my question is, is there a better, more efficient way to solve this problem?
Yes, use a Set for your DuplicateCheck which gives you O(1) access by id:
const duplicateCheck = new Set
const finalResult = []
for (const object of arrayOfObjects) {
if (!duplicateCheck.has(object.id)) {
duplicateCheck.add(object.id)
finalResult.push(object)
}
}
You could iterate over the array and store the id in and object (hash table) and then check if exist. Something like:
const DuplicateCheck = {}
const FinalResult = []
for (let i = 0; i < ArrayOfObjects.length; i++) {
let currentId = ArrayOfObjects[i].id
if (!DuplicateCheck[currentId]) {
DuplicateCheck[currentId] = 1
FinalResult.push(ArrayOfObjects[i])
}
}
And you will receive all unique objects in the FinalResult
You could keep usedIds as object properties and add to the filtered array only if the object has no such property or just add your items in Set if that's a possibility for you. Set as a data structure can only store non-duplicates.
Without Set:
const filteredArray = [];
const usedIds = {};
for (const item of array) {
if (!usedIds[item.id]) {
usedIds[item.id] = true;
filteredArray.push(item);
}
}
With Set:
const filteredArray = [];
const usedIds = new Set();
for (const item of array) {
if (!usedIds.has(item.id)) {
usedIds.add(item.id);
filteredArray.push(item);
}
}
Runnable example:
const array = [
{
id: 1,
stuff: 'stuff',
moreStuff: 'moreStuff'
},
{
id: 6,
manyStuff: 'manyStuff',
stuffing: 'stuffing'
},
{
id: 4,
yayStuff: 'yayStuff',
stuff: 'stuff'
},
{
id: 6,
manyStuff: 'manyStuff',
stuffing: 'stuffing'
}
];
const filteredArray = [];
const usedIds = {};
for (const item of array) {
if (!usedIds[item.id]) {
usedIds[item.id] = true;
filteredArray.push(item);
}
}
console.log(filteredArray);
You can also use a Map to filter out duplicates. Contrary to the Set approach by Bergi this solution leaves the last version of the duplicate, since it overrides the key/value-pair with the same key.
const objectsById = new Map(arrayOfObjects.map(object => [object.id, object]));
const finalResult = Array.from(objectsById.values());
The code above does need to iterate the collection 2 times. Once using map to create key/value-pairs and once when the created array is converted to a Map.
When the resulting objectsById is created we'll have to iterate over the values to convert them back to array.
In total this means between 2 and 3 iterations over the full collection, which more often then not still a lot faster then solutions using find. Since that iterates over the array every time it is called.
You can reduce the amount of iterations by 1 if you omit the map call and manually insert the elements in objectsById:
const objectsById = new Map();
for (const object of arrayOfObjects) {
objectsById.set(object.id, object);
}

How do I append the number of a duplicate in an array as a prop of the array?

I have an array like [{type: car}, {type: van}, {type: truck}, {type: car}]
I need to return an array that looks like: [{type: car, count: 1}, {type: van, count: 1}, {type: truck, count: 1}, {type: car, count: 2}]
in the returned array, there is a new prop that stores what number of instance that value for type is. i.e. how many times has this value shown up in the array.
This is for an array that will be rendered and I want to include a number next to values that have duplicates in the array. I'm working with array methods reduce and map, maybe I will need a find function, I'm not sure
The main diff from the link for the possible duplicate and my question is that the linked question/answer results in an array with each unique value and a count of the duplicates whereas I would like my og array with an additional prop that is the number of times this value has shown up in the array. so the first instance of a value would be have count: 1, the second instance count: 2 and so on.
I've tried using a reduce function to give me a count of the duplicates for each value but now that I'm mapping through the original array I'm having trouble determining if i'm on the first, second, ect of that value. for example I can find if there are 3 of the current value i'm on in my array.map but I don't know if its the first or second or third one in that array.
here is what I have:
let countArray = this.props.clauses.reduce((prev, cur) => {prev[cur.leaseClauseType] = (prev[cur.leaseClauseType] || 0) + 1; return prev; }, {});
this.props.clauses.map((c: AnyObject, index: number)=> {
//Here i can append the count from countArray to each value but I'd like to know if its the first, second ect. of that value
}
This can be quickly done using two loops:
const addCountProp = function (arr) {
// to store number of instances for each type
const typeMap = {};
for (let i = 0; i < arr.length; i++) {
const elem = arr[i];
if (typeMap[elem.type] === undefined)
typeMap[elem.type] = 0;
typeMap[elem.type] += 1;
}
// add 'count' property to each element of the array
for (let i = 0; i < arr.length; i++) {
const elem = arr[i];
elem.count = typeMap[elem.type];
}
return arr;
};
You could take a Map for counting
var data = [{ type: 'car' }, { type: 'van' }, { type: 'truck' }, { type: 'car' }],
result = Array.from(
data.reduce((m, { type }) => m.set(type, (m.get(type) || 0) + 1), new Map),
([type, count]) => ({ type, count })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

JavaScript: Find all parent indexes in a nested object

I have the following object:
const obj = {
alphas: {
top: [{name: "q"}, {name: "w"}],
middle: [{name: "a"}, {name: "s"}],
bottom: [{name: "z"}, {name: "x"}],
},
numbers: {
low: [{name: "1"}, {name: "2"}, {name: "3"}],
high: [{name: "1000"}, {name: "2000"}],
}
}
I need the nested indexes of the name.
For instance, if I'm looking for "s" the result should be the array [0, 1]:
0 because "s" is in the first category (alphas)
1 because "s" is in the second subcategory (middle).
I'm able to find the indexes in separate loops:
const categoryIndex = Object.keys(obj).findIndex(
cat => Object.keys(obj[cat]).some(
subcat => obj[cat][subcat].some(
key => key.name === "s")));
const categoryName = Object.keys(obj)[categoryIndex];
const subcategoryIndex = Object.keys(obj[categoryName]).findIndex(
subcat => obj[categoryName][subcat].some(key => key.name === "s"));
const result = [categoryIndex, subcategoryIndex];
https://jsfiddle.net/7w523ojn/
Having two separate loops may cost too much so I'm looking for a way to get indexes at once. Something like this:
[categoryIndex , subcategoryIndex] = ...
Is there a way to get nested indexes at once?
Solutions involving Lodash, Ramda and whatnot are also welcome.
Thank you.
If I understand you question correctly, then this can be achieve via a recursive search and return function, as detailed below:
const obj={alphas:{top:[{name:"q"},{name:"w"}],middle:[{name:"a"},{name:"s"}],bottom:[{name:"z"},{name:"x"}]},numbers:{low:[{name:"1"},{name:"2"},{name:"3"}],high:[{name:"1000"},{name:"2000"}]}};
const recursiveSearch = (object, searchFor) => {
let index = 0;
for (const key in object) {
const value = object[key];
if (Array.isArray(value)) {
/* If object value is an array, iterate through it and
search for item with name field matching searchFor
character */
const array = value;
for (let i = 0; i < array.length; i++) {
/* If array item's name matches searchFor charater
then return this as the first entry in our results
array */
if (array[i].name === searchFor) {
return [index, i];
}
}
} else if (typeof object === "object") {
/* If object value is object, then recursivly search
through children for a result. If an result is found,
prepend the current key's index to the result array */
const result = recursiveSearch(value, searchFor);
/* Array.isArray() tells us this call to recursiveSearch()
found a result, so we are to return from this call */
if (Array.isArray(result)) {
return [index].concat(result);
}
}
index++;
}
};
console.log('indicies for "s":', recursiveSearch(obj, 's'));

How to find object value from property path in Javascript object

I explain the problem with sample.
For example i have javascript object. It looks like:
var trial= { points:[{x:1,y:2},{x:5,y:2},{x:3,y:4}] , obj:{id:5,name:"MyName"} }
I use deep diff module to find difference between two json array. Then it find the differences and find difference path. If value of x is changed, then it finds.
For example
path = ["points",0,"x"]
or
path= ["obj","name"]
So my question is that how to generate json object from these path.
For example i have to generate that
trial.points[0].x or trial.obj.name
How to do that ? thank you for your answer.
You can use the following logic:
for(var i = 0, result = trial; i < path.length; i++) {
result = result[path[i]];
}
You can use array#reduce and pass your object and path as variables. Array#reduce will return the value corresponding to a path.
var trial= { points:[{x:1,y:2},{x:5,y:2},{x:3,y:4}] , obj:{id:5,name:"MyName"} },
path1 = ["points",0,"x"],
path2= ["obj","name"],
valueAtPath = (object, path) => path.reduce((r,p) => r[p], object);
console.log(valueAtPath(trial, path1));
console.log(valueAtPath(trial, path2));
You can do like this:
var trial= { points:[{x:1,y:2}, {x:5,y:2},{x:3,y:4}] , obj:{id:5,name:"MyName"}};
var path = ["points", 0, "x"];
var object = trial;
path.map(field => object = object[field]);
console.log(object);
path = ["obj", "name"];
var object = trial;
path.map(field => object = object[field]);
console.log(object);
Disclosure: I am the author of both deep-diff and json-ptr.
To build an object out of an array of attribute names such as [ "points", 0, "x" ], use a good JSON Pointer implementation, since most of them have convenience methods for translating these paths, and applying values to object graphs.
For example (Node.js):
const diff = require("deep-diff");
const ptr = require("json-ptr");
let original = {
points: [
{ x: 1, y: 2 },
{ x: 5, y: 2 },
{ x: 3, y: 4 }
],
obj: {
id: 5,
name: "MyName"
}
};
let modified = JSON.parse(JSON.stringify(original));
modified.points[0].x = 7;
modified.obj.name = "Wilbur Finkle";
const differences = diff(original, modified);
// Produce an object that represents the delta between original and modified objects
const delta = differences.reduce((acc, record) => {
// Only process edits and newly added values
if (record.kind === "E" || record.kind === "N") {
ptr.set(
acc, // target
ptr.encodePointer(record.path), // pointer; from path
record.rhs, // modified value
true // force; creates object graph
);
}
return acc;
}, {});
console.log(JSON.stringify(delta, null, " "));
Produces:
{ points: [ { x: 7 } ], obj: { name: "Wilbur Finkle" } }

Find highest recurring duplicates in a javascript array

I have an array with several names like :
[mike,bob,john,john,rick,bob]
Can someone please tell me how is the most efficient way to find which name has been repeated the most?
Generate an object with count using Array#reduce method, later sort the property name array(get using Object.keys method) based on the count using Array#sort method and finally get the first element.
var arr = ['mike', 'bob', 'john', 'john', 'rick', 'bob'];
// generate an object which holds the count
var obj = arr.reduce(function(o, v) {
// define the property if not defined
o[v] = o[v] || 0;
// increment the count
o[v]++;
// return the object reference
return o;
// set initial value as an empty object
}, {});
// sort based on the count, descending order
// and get first
console.log(
Object.keys(obj).sort(function(a, b) {
return obj[b] - obj[a];
})[0]
)
// or using reduce, reduce into a single value
// which holds the highest value
console.log(
Object.keys(obj).reduce(function(a, b) {
return obj[b] > obj[a] ? b : a;
})
)
The standard solution to find duplicates (in O(n) time) is to walk linearly through the list and add each item to a set. Each time, before doing so, check if it is already in the set. If it is, you've found a duplicate.
names = [ 'mike', 'bob', 'john', 'john', 'rick', 'bob'];
seen = new Set();
names.forEach(function(item, index, array) {
if (seen.has(item)) {
console.log(item + " is a duplicate");
} else {
seen.add(item);
}
});
Alternately, you can sort in O(n log(n)) time, and save the extra space that was used above, by sorting and checking pair-wise as you iterate over the array:
names = [ 'mike', 'bob', 'john', 'john', 'rick', 'bob'];
names.sort().forEach(function(item, index, array) {
if ((index > 0) && (array[index - 1] == item)) {
console.log(item + " is a duplicate");
}
});
function findDup(arr){
var obj={};
for(var i of arr){
if(obj[i]==1){
console.log(i);
}
obj[i]=1;
}
}
findDup([ 'mike', 'bob', 'john', 'john', 'rick', 'bob']);
You could find with the help of map as follows
function findDuplicates() {
var name, names = ['mike', 'bob', 'john', 'john', 'rick', 'bob'];
var map = new Map();
var max = 1;
var maxRecurringString = "";
for(name of names) {
if(map.get(name) === undefined) {
map.set(name, 1);
} else {
var count = map.get(name);
count = count+1;
map.set(name, count);
if(max < count) {
max = count;
maxRecurringString = name;
}
}
}
console.log("Maximum recurring string is ", maxRecurringString, ". Max number of times :" + max);
}
findDuplicates();
This snippet prints the first string which appears maximum number of times. Say for example in the above example bob and john has appeared twice. If you want all the strings to be printed which has appeared maximum number of times you can iterate for the map whose count is the max count to print all the strings.
If you're looking for the most efficient way to do this, talking about performance, the best way is to loop over the array only once.
Array#reduce, Array#sort, Array#forEach will loop over the entire array, so if you're concerned about performance (specially working with considerable amount of data), avoiding those is the best practice
var findHighestRecurring = function(arr) {
arr.sort();
var i = arr.length;
var max = { item:"", count: 0 };
var last = { item:"", count: 0 };
var validate = function() {
if (last.count > max.count) {
max.item = last.item;
max.count = last.count;
}
}
while (i--) {
var curr = arr[i];
if (last.item !== curr) {
validate();
last.item = curr;
last.count = 0;
}
last.count++;
}
validate();
return max;
}
var sample = ["mike","bob","john","bob","john","rick","bob"];
var result = findHighestRecurring(sample);
console.log(result);

Categories

Resources