Array with object index not getting as expected [duplicate] - javascript

This question already has answers here:
Object comparison in JavaScript [duplicate]
(10 answers)
How to determine equality for two JavaScript objects?
(82 answers)
Closed 3 years ago.
I have an array
let arr = []
I am inserting an object into it
let a = {name:"a", age: 20}
arr.push(a);
I am checking index with below with same object assigned to different variable
let b = {name:"a", age:20}
I tried
arr.indexOf(b);
I am getting -1 I expect 0.
Please guide me

JS does object comparison (and search) by reference, not by value. Hence: for foo == bar to be true, foo and bar must be the same object, not just an object with the same properties and values:
console.log({name:"a", age:20} == {name:"a", age:20});
Hence instead of indexOf, you need something like findIndex.
let arr = [];
let a = {name:"a", age: 20};
arr.push(a);
let b = {name:"a", age:20}
const index = arr.findIndex(item => item.name === b.name && item.age === b.age);
console.log(index);

This is because, objects are reference type, the variable a and b points to two different memory locations. Thus a != b
let a = {name:"a", age: 20};
let b = {name:"a", age:20}
console.log(a == b);

JavaScript can not compare objects by default. Use Array.findIndex instead:
let arr = []
let a = {name:"a", age: 20}
arr.push(a);
let b = {name:"a", age:20}
console.log(arr.findIndex(elem => elem.name == b.name && elem.age == b.age));

Yes because Js compare by using reference not by value
let arr = [];
let a = {
name: "a",
age: 20
};
arr.push(a);
let b = {
name: "a",
age: 20
};
console.log(arr.indexOf(b));
let arr = [];
let a = {
name: "a",
age: 20
};
arr.push(a);
let b = a
console.log(arr.indexOf(b));

Related

Compare two arrays of objects and if value for a certain key is missing, add an object with that value to the array - two ways

I have one array that has objects whose keys match another array of objects with the same keys, and the value for the first key is a Month index (0 = January, 1 = February, etc.) and the points may cross a year division (a:10, a:11, a:0, a:1)
However the first array may not have the same number of objects, and the values for the key on one object may not exist in the other object on the same key, and vice-versa.
Assuming both arrays are already ordered correctly, I want to compare those two arrays, and if one array is missing an object with a value for a key that the other one has, I want to add a new object to the first array with the same key and value, in the same position/index within the array that doesn't have it, as it is in the array that does have it.
let arr1 = [{a:0, b:1},{a:1, b:3},{a:3, b:18},{a:4, b:2}]
let arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1}]
arr1 is missing objects with an a value of 10, 11, and 2 that exist in arr2, and arr2 is missing objects with an a value of 3 and 4 that exist in arr1
What I want to end up with is:
arr1 = [{a:10, b:0},{a:11, b:3},{a:0, b:1},{a:1, b:3},{a:2, b:0},{a:3, b:18},{a:4, b:2}]
arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1},{a:3, b:0},{a:4, b:0}]
Now arr1 has new items/objects for a:10, a:11, and a:2 while
arr2 has new items for a:3 and a:4, all of which have a b value of 0;
I've tried plotting this out on paper to see logically what I would do physically, but I just can't get my head around it, so a "For Dummies" answer would be really helpful. I thought I had learned enough to be employable, but this is really sticking me up, and it's just for a simple HTML5 canvas line graph. I'm getting data from a DB to compare two lines on the same graph, where there may not be data for one month for one type of data, but there is for the other type of data. And for those spots that don't exist in one or the other, I want to drop the line down to 0 on the Y axis, then back up to the next value. https://github.com/rmgreenstreet/custom-forms/blob/master/public/javascripts/canvasRender.js
Having just month indexes is not OK for proper sorting.
Just add some information about year and it will be done easily.
Without sorting it may look like:
// loop elements of the arr2 with reduce,
// if there is any element in arr1 with the same value of key 'a',
// result is the same as on the previous step
// if there is no elements of that kind, add new object {'a': arr2['a'], 'b' : 0} into arr1
function newArray (arr1, arr2) {
return arr2.reduce((result, obj2) => {
if (arr1.some(obj1 => obj1['a'] === obj2['a'])) {
return result;
}
return [...result, {['a'] : obj2['a'], 'b':0}];
}, arr1)
}
// now you can assign the result of newArray() to new variables
const arr1_ = newArray(arr1, arr2)
const arr2_ = newArray(arr2, arr1)
OP's final working function (having changed the a value to be a mm/yyyy string:
function equalize(arr1, arr2) {
let newArr = arr2.reduce(function (result, obj2) {
if (arr1.some(obj1 => obj1['a'] === obj2['a'])) {
return result;
}
return [...result, {'a' : obj2['a'], 'b':0}];
}, arr1);
newArr.sort(function (a, b) {
console.log(`a: ${a}`)
a = a.x.split('/');
b = b.x.split('/')
return new Date(a[1], a[0], 1) - new Date(b[1], b[0], 1)
});
return newArr;
};
The main annoyance of this task is checking for presence of items with certain values of a. A naive straightfoward solution would require iterating over arr2 for evey item of arr1 and vice versa, which makes it O(n2), i.e. slow.
An alternative approach suggests employing objects as fast lookup maps. The idea is to turn your array inside out, use as as keys and bs as values.
let arr1 = [{a:1, b:1},{a:2, b:3},{a:4, b:18},{a:5, b:2}]
let arr2 = [{a:2, b:2},{a:3, b:4},{a:4, b:8},{a:6, b:5},{a:7, b:1}]
// Using objects as lookup maps.
let m1 = {}
let m2 = {}
// Filling the maps.
// The 'a' becomes a key, the 'b' becomes a value.
arr1.forEach(v => m1[v.a] = v.b)
arr2.forEach(v => m2[v.a] = v.b)
// Iterating over the keys of m1, checking if m2 has that key,
// if not - adding it with a value of 0.
for (let f in m1) m2[f] || (m2[f] = 0)
// The same goes for m2 -> m1.
for (let f in m2) m1[f] || (m1[f] = 0)
// At this point both m1 & m2 have all the keys from both arrays without gaps.
let res1 = []
let res2 = []
// Assembling the resulting arrays.
for (let f in m1) res1.push({a: f, b: m1[f]})
for (let f in m2) res2.push({a: f, b: m2[f]})
Pardon my inclination for one-liners.
Here is my solution. I am using lodash for helper functions.
function combineArrays (a, b) {
Object.keys(b).forEach(function (k) {
const key = parseInt(k);
if (!a[key]) {
a[key] = b[key];
a[key].b = 0;
}
});
return _.values(a);
}
Working code snippet
// const arr1 = [{ a: 1, b: 1 }, { a: 2, b: 3 }, { a: 4, b: 18 }, { a: 5, b: 2 }];
// const arr2 = [{ a: 2, b: 2 }, { a: 3, b: 4 }, { a: 4, b: 8 }, { a: 6, b: 5 }, { a: 7, b: 1 }];
let arr1 = [{a:0, b:1},{a:1, b:3},{a:3, b:18},{a:4, b:2}]
let arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1}]
const arr1Map = _.keyBy(arr1, 'a');
const arr2Map = _.keyBy(arr2, 'a');
function combineArrays(a1Map, a2Map) {
const a = _.cloneDeep(a1Map);
const b = _.cloneDeep(a2Map);
Object.keys(b).forEach(function(k) {
const key = parseInt(k);
if (!a[key]) {
a[key] = b[key];
a[key].b = 0;
}
});
return _.values(a);
}
console.log(combineArrays(arr1Map, arr2Map));
console.log(combineArrays(arr2Map, arr1Map));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
You should loop through the first array, and check if each key is in the second array. If it's not you should insert an item using arr.splice().
If you know both lists are sorted, ie the keys are in order, then you can also don't need to check the whole array for the new key.
let j = 0;
for (let i = 0; i < arr1.length; i++) {
let possiblyMissingKey = arr1[i].a;
while (arr2[j].a < possiblyMissingKey && j < arr2.length)
j++;
if (arr2[j].a != possiblyMissingKey) {
let itemToInsert = {a:possiblyMissingKey, b:0};
arr2.splice(j, 0, itemToInsert);
}
}
After you go through the first array, do the second array.

behaviour of Array.indexOf() [duplicate]

This question already has answers here:
Why Array.indexOf doesn't find identical looking objects
(8 answers)
Closed 3 years ago.
Array.IndexOf() function behaves different for strings and objects.
stringBehaviour();
objectBehaviour();
function stringBehaviour() {
let s1 = "helmet";
let s2 = "trousers";
let collection = [];
collection.push(s1);
collection.push(s2);
console.info(collection.indexOf("trousers"));
};
function objectBehaviour() {
let obj1 = {item: "helmet"};
let obj2 = {item: "trousers"};
let collection = [];
collection.push(obj1);
collection.push(obj2);
console.info(collection.indexOf({item: "trousers"}));
};
what can i do to have indexOf() not looking for the same instance but for equality of the objects elements so that the function objectBehaviour() would return 1?
You could use findIndex instead and pass a function which compares objects by their properties.
stringBehaviour();
objectBehaviour();
function stringBehaviour() {
let s1 = "helmet";
let s2 = "trousers";
let collection = [];
collection.push(s1);
collection.push(s2);
console.info(collection.indexOf("trousers"));
};
function objectBehaviour() {
let obj1 = {item: "helmet"};
let obj2 = {item: "trousers"};
let collection = [];
collection.push(obj1);
collection.push(obj2);
console.info(collection.findIndex(el => isIdentical(el, { item: "trousers"})));
};
function isIdentical(current, given) {
return Object.keys(given).every((key) => current[key] === given[key]);
}

How to compare 3 value in this way? [duplicate]

This question already has answers here:
Javascript compare 3 values
(7 answers)
Closed 4 years ago.
I need a way to compare 3 values in a short way like this:
'aaa'=='aaa'=='aaa'
false
but as you can see, it doesn't work. Why?
With 2 values it does work obviously:
'aaa'=='aaa'
true
Comparing of first two values evaluates to true and then that true is compared with "aaa" which evaluates to false.
To make it correct you can write:
const a = 'aaa';
const b = 'aaa';
const c = 'aaa';
console.log(a === b && b === c); //true
if you have those strings stored in variables you can do
let a = 'aaa', b = 'aaa', c = 'aaa'
console.log(a === b && b === c) // true
The expression 'aaa'=='aaa'=='aaa' is evaluated as ('aaa'=='aaa')=='aaa'.
The sub-expression in parentheses evaluates to true and it becomes true=='aaa' which is false because when compares two values of different types, JavaScript first converts one of them or both to a common type. It converts the boolean true to the number 1 and the string 'aaa' to the number 0 which are, obviously, not equal.
What you need is
console.log('aaa'=='aaa' && 'aaa'=='aaa')
You can put all values in an Array and then use Array.prototype.every() function to test if all satisfy the condition defined in the callback you pass to it:
let a = 'aaa', b = 'aaa', c = 'aaa'
let arr = [a, b, c]
let arr2 = [1, 2, 1]
console.log(arr.every(i => [arr[0]].includes(i)))
console.log(arr2.every(i => [arr2[0]].includes(i)))
Also, you may get the unique values from a given sequence, and if you get a single element is because all equal:
const same = xs => new Set (xs).size === 1
const x = 'aaa'
const y = 'aaa'
const z = 'aaa'
const areSame = same ([ x, y, z ])
console.log(areSame)
const x_ = 'aaa'
const y_ = 'bbb'
const z_ = 'aaa'
const areSame_ = same ([ x_, y_, z_ ])
console.log (areSame_)
With variadic arguments
const same = (...xs) => new Set (xs).size === 1
const x = 'aaa'
const y = 'aaa'
const z = 'aaa'
const areSame = same (x, y, z)
console.log(areSame)
const x_ = 'aaa'
const y_ = 'bbb'
const z_ = 'aaa'
const areSame_ = same (x_, y_, z_)
console.log (areSame_)

Merge Arrays in Javascript / Node.js

I was wondering if there was a way to merge arrays in this way in javascript
Array1 = ['1', '234', '56']
Array2 = ['1', '2', '45', '56']
Wanted outcome = ['11', '2342', '5645', '56']
Is there a way built into the language to do this?
Use .map to transform one array into another:
const Array1 = ['1', '234', '56']
const Array2 = ['1', '2', '45', '56'];
const merged = Array2.map((arr2elm, i) => (Array1[i] ? Array1[i] : '') + arr2elm);
console.log(merged);
An alternative using the function Array.from
The function Array.from accepts three params:
arrayLike An array-like or iterable object to convert to an array.
mapFn Map function to call on every element of the array.
Optional.
thisArg Value to use as this when executing mapFn Optional.
This approach passes an object with the required property length (array-like or iterable object) with the max length from both arrays and the callback will provide two params:
value (in this case is undefined) from an array
The current index.
Basically, the callback concatenates two values and both operands check for the current value at a specific index because not necessarily the arrays have the same length.
var arr1 = ['1', '234', '56'],
arr2 = ['1', '2', '45', '56'],
newArray = Array.from({length: Math.max(arr1.length, arr2.length)},
(_, i) => ((arr1[i] || '') + (arr2[i] || '')));
// ^ ^
// | |
// | +---- This is the current index.
// |
// +---- In this case, the value is undefined
// and is unnecessary to accomplish your
// scenario.
console.log(newArray);
No native feature for that, but here is a way to achieve it;
var a1 = ['1', '234', '56'];
var a2 = ['1', '2', '45', '56'];
var length = Math.max(a1.length, a2.length)
var merge = new Array(length).fill().map((el, index) => {
return (a1[index] || '') + (a2[index] || '')
})
console.log(merge)
This code will provide you with the correct answer regardless of which array is bigger.
EDIT:
As per commenter suggestion, by using a for loop you wont waste memory resources by creating an array just for iteration purposes.
var a1 = ['1', '234', '56'];
var a2 = ['1', '2', '45', '56'];
var length = Math.max(a1.length, a2.length)
var merge = []
for (var i = 0; i < length; i++) {
merge.push((a1[i] || '') + (a2[i] || ''))
}
console.log(merge)
And, even faster if you replace the .push() with an assignment:
var a1 = ['1', '234', '56'];
var a2 = ['1', '2', '45', '56'];
var length = Math.max(a1.length, a2.length);
var merge = new Array(length);
for (var i = 0; i < length; i++) {
merge[i] = (a1[i] || '') + (a2[i] || '');
}
console.log(merge);
function mergeArrays(array1, array2) {
const count = array1.length > array2.length
? array1.length
: array2.length;
const result = [];
for (let i = 0; i < count; i++) {
result.push(`${ array1[i] || '' }${ array2[i] || '' }`);
}
return result;
}
A side-note: don't use uppercase naming for your identifiers unless they are classes.
You can do like below
let Array1 = ['1', '234', '56','11','11','22'];
let Array2 = ['1', '2', '45', '56'];
let new_arr=[];
new_arr=Array1.map((object,i) => object + Array2[i]).concat(Array2.splice(Array1.length,1));
//remove undefined due to variable size
let new_arr_str=new_arr.join(",").replace(/undefined/g,'');
console.log(new_arr_str.split(","));
I have removed undefined variable if array1 is larger than array 1 using string functions

Returning value with variable’s name

var a = 1;
var b = 2;
var c = 3;
var d = Math.max(a, b, c);
return d;
This code is returning 3. I need a result with not only the computed value, but with a variable name also.
Desired output: c - 3.
Unfortunately, the names of variables are not easily retrievable in JavaScript.
Such a solution is not scalable either.
However, an adjustment to the data structures that you leverage can solve this.
Note the use of Object, Object.entries() and Array.prototype.reduce() in the example below.
// Pairs.
const pairs = {
a: 1,
b: 2,
c: 3
}
// Get Max Pair.
const getmaxpair = (pairs) => Object.entries(pairs).reduce((max, pair) => !max && pair || pair[1] > max[1] ? pair : max, false)
// Max Pair.
const [key, value] = getmaxpair(pairs)
// Log.
console.log('max pair:', `${key} ${value}`) // c 3
If you are okay with storing those values in an object, you can loop through the keys and compare their values while storing the name of the key.
var values = { a: 1, b: 2, c: 3 }
var maxName;
var max;
for(var key in values) {
if(max === undefined || max < values[key]){
maxName = key;
max = values[key];
}
}
console.log('Max: ' + maxName + ' - ' + values[maxName]);
var values = { a: 1, b: 2, c: 3 }
var maxName;
var max;
for(var key in values) {
if(max === undefined || max < values[key]){
maxName = key;
max = values[key];
}
}
console.log('Max: ' + maxName + ' - ' + values[maxName]);
You can do this easily with javascript objects or maps
var list = {"a": 1, "b": 2, "c": 3};
var d = Math.max(list.a, list.b, list.c);
// this will return an array: ["a", "b", "c"]
keys = Object.keys(list);
// do a reverse lookup here to find which key your value belongs to
// eg. if list[keys[counter]] == d { your code here }
Here is another way of doing reverse lookups
https://stackoverflow.com/a/9907509/9310329
Hope this helps
Although this could be more easily achieved using a map (see other answers) in a realistic use case.
What you're asking for is actually also possible (inspired from https://stackoverflow.com/a/42791996/7988438 )
let a = 1;
let b = 2;
let c = 3;
let d = Math.max(a, b, c);
let varToString = varObj => Object.keys(varObj)[0];
if (a == d) {
console.log(varToString({a}),d)
}
if (b == d) {
console.log(varToString({b}),d)
}
if (c == d) {
console.log(varToString({c}),d)
}
Quite ugly, but gets the job done.
While the magic you think about is possible to some extent with iterating over object keys as shown in the other answers, often you just want an array of objects instead, where the keys are known, and the values vary:
var persons=[
{name: "John", age: 52},
{name: "Jane", age: 35},
{name: "Peter", age: 86},
{name: "Susan", age: 72}
];
var eldest=persons[0];
persons.forEach(function(person){
if(person.age>eldest.age)eldest=person;
});
console.log(eldest);

Categories