Javascript - Modifying two arrays in while loop, both arrays retaining second value - javascript

I have an array data, that contains a nodeData object, which contains an id
I make 2 copies of the array:
const dropAboveData = data.slice();
const dropBelowData = data.slice();
and then I try to modify both copies of my 2 copied arrays differently
for(let i = 0; i<data.length; i++){
dropAboveData[i].nodeData.id = -1;
dropBelowData[i].nodeData.id = -2;
}
So for example if each record in data had data[i].nodeData.id = 0, at the end i would expect dropAboveData to contain all -1 for id, and dropBelowData to contain all -2 for id.
But instead it seems like data, dropAboveData, and dropBelowData all become arrays filled with -2.
Why is this happening? I though slice() makes a copy of the array so that i'm not accessing the same object?

Slice makes a shallow copy
The slice() method returns a shallow copy of a portion of an array
into a new array object selected from begin to end (end not included).
The original array will not be modified.
You could copy an array of objects like this:
var data = [{
'a': '0'
}, {
'b': '1'
}, {
'c': '2'
}]
dropAboveData = []
dropBelowData = []
data.map(o => {
dropAboveData.push(JSON.parse(JSON.stringify(o)));
dropBelowData.push(JSON.parse(JSON.stringify(o)));
});
dropAboveData[0] = 1; //<-- modify only dropAboveData
dropAboveData[1].b = 99;//<-- modify only dropAboveData
console.log(dropAboveData)
console.log(dropBelowData)

as charieftl pointed out i was not copying the objects in my array (nodeData objects).
const dropAboveData = data.map(item => item.nodeData.id = -1);
const dropBelowData = data.map(item => item.nodeData.id = -2);
did what I wanted.

Related

From an array, find the array that has the true values for the objects

Given array:
const array = [{1: true},{2: false},{3: true},{}.....];
Filter the given array by only including objects with values of true.
Looking for the shortest solution.
const onlyTrue = array.filter((el, ind) => el[ind + 1] === true);
This will work only if indexes in array objects are ordered and starting from 1, as it is in your example.
Assumptions (based on what's in your example):
Each object in the array only has at least 1 property in it
The first property on each object is the one we care about
The property in each object is different every time
const array = [{1: true},{2: false},{3: true}];
const results = array.filter(item => Object.values(item)[0]);
If you want to avoid any false positives from truth-y values (see https://developer.mozilla.org/en-US/docs/Glossary/Truthy), then change the filter call to this instead:
const results = array.filter(item => Object.values(item)[0] === true);
const array = [{1: true},{2: false},{3: true}];
const array2 = [];
array.forEach(filterTrue);
function filterTrue(item){
for(x in item){
if(item[x]===true){
array2.push(item);
}
}
}
console.log(array2);
Hope this helps you.

how to filter arrays inside an object and return the array with the most items inside?

I'm developing an app that requests data from a third-party source by API calls.
one of my relevant data is located in arrays inside an object.
The trick here is that in some calls I'm making to get the data that object contains a single array and in other calls, it contains multiple arrays.
I need the data of the array with the most items inside.
in most cases that object contains 2 arrays inside - in that case, my code is working well and I can filter my relevant array most of the time it's the second array - array[1].
but when that object contains a single array inside - that is where I'm struggling to achieve the data.
(The arrays names are random numbers in each JSON I'm getting so I need a generic solution).
here is example
object{
"154987" [150 items],
"754896" [13 items],
"265489" [11 items]
}
Here is what I have in my code so far which not working with only single array
function getCurrentBsrByObjectKeyIndex(index) {
product.bsrObjectKey = (Object.keys(asinData.products[0].salesRanks)[index]);
product.bsrHistory = asinData.products[0].salesRanks[product.bsrObjectKey];
product.currentBsr = product.bsrHistory[product.bsrHistory.length-1];
}
function correctBsrObjectKey() {
getCurrentBsrByObjectKeyIndex(1);
if (product.bsrHistory.length < 15){
getCurrentBsrByObjectKeyIndex(0);
}
}
correctBsrObjectKey();
The approach is as follows.
Directly access a list (an array) of all of the objects first level array's by using Object.values
Iterate the list/array via Array.prototype.reduce
The reducer or callback function compares always two values, the first argument which was passed by the callbacks previous iteration as the back then array of maximum length and the 2nd argument which is the array of the current iteration step.
The function then compares the two arrays and returns (to itself into the next iteration) the new or old (depending on comparison) array of maximum length.
Using reduce one also gets some of its default behaviors for free ...
If the array only has one element (regardless of position) and no initialValue is provided, or if initialValue is provided but the array is empty, the solo value will be returned without calling callbackFn.
function getArrayOfMaximumLength(obj) {
return Object
.values(obj)
.reduce((maxArr, arr) =>
// this implementation breaks at
// an entirely emtpy `values` array
((maxArr.length > arr.length) && maxArr) || arr
// // this implementation does never break but always
// // at least returns an empty array ... [] ...
// // which might unwantedly shadow the consumption of
// // broken data structures
//
// ((maxArr.length > arr.length) && maxArr) || arr, []
);
}
const sample_1 = {
"754896": ['foo', 'bar', "baz"],
"154987": ['foo', 'bar', "baz", "biz", "buz"],
"265489": ['foo'],
};
const sample_2 = {
"265489": ['foo'],
"754896": ['foo', 'bar', "baz"],
};
const sample_3 = {
"754896": ['foo', 'bar', "baz"],
};
const invalid_sample = {};
console.log(
'getArrayOfMaximumLength(sample_1) ...',
getArrayOfMaximumLength(sample_1)
);
console.log(
'getArrayOfMaximumLength(sample_2) ...',
getArrayOfMaximumLength(sample_2)
);
console.log(
'getArrayOfMaximumLength(sample_3) ...',
getArrayOfMaximumLength(sample_3)
);
console.log(
'getArrayOfMaximumLength(invalid_sample) ...',
getArrayOfMaximumLength(invalid_sample)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
var object = {
"154987": [1, 2, 3],
"754896": [1, 2],
"265489": [5, 4, 3, 2, 1, 2, 4, 5]
}
var keys = Object.keys(object);
var highestArray = [];
for (var i = 0; i < keys.length; i++) {
var obj = object[keys[i]];
if (Array.isArray(obj) && obj.length > highestArray.length)
highestArray = obj;
}
console.log(highestArray);
Get all property name from your object. Then iterate through it to find the array with highest number of items in it.

How to group multiple sets of duplicate integers in an array into their own array of arrays?

I am trying to split an array of integers into an array of arrays by duplicate values. The original array is composed of a list of 6 digit integers, some of these integers come in pairs, others come in groups of 3 or 4s. I'd like to get these duplicates pushed to their own arrays and have all of these arrays of duplicates composed into an array of arrays that I can later loop through.
I've looked on in the lodash library for some method or combination of but can't quite find anything that seems to work. I've also tried a few different configurations with nested for loops but also am struggling with that.
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = [ ];
for(i=0; i< files.length; i++){
let first6 = files[i].substring(0, 6);
first6Array.push(first6);
};
console.log(first6Array);
example output of first6Array:
[ '141848',
'141848',
'141848',
'142851',
'142851',
'143275',
'143275']
I'd like to end up with something like
let MasterArray = [[141848,141848,141848],[142851,142851],[143275,143275]];
You can use new Set() to filter out the duplicates.
Then you use the unique Array and filter for every value.
const firstArray = [ '141848', '141848', '141848', '142851', '142851', '143275', '143275'];
const numberArray = firstArray.map(Number);
const masterArray = [];
const unique = new Set (numberArray); // Set {141848, 142851, 143275}
unique.forEach(u => {
masterArray.push(numberArray.filter(e => e === u));
});
console.log(masterArray);
Using lodash, you can create a function with flow:
map the items by truncating them and converting to numbers.
groupBy the value (the default).
convert to an array of arrays using values.
const { flow, partialRight: pr, map, truncate, groupBy, values } = _;
const truncate6 = s => truncate(s, { length: 6, omission: '' });
const fn = flow(
pr(map, flow(truncate6, Number)),
groupBy,
values,
);
const firstArray = [ '141848abc', '141848efg', '141848hij', '142851klm', '142851opq', '143275rst', '143275uvw'];
const result = fn(firstArray);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Use reduce to create an object of arrays, indexed by number, and push to the associated array on each iteration (creating the array at the key first if needed), then get the values of the object:
const directory = "X/";
const files = fs.readdirSync(directory);
const output = Object.values(
files.reduce((a, file) => {
const num = Number(file.slice(0, 6));
if (!a[num]) a[num] = [];
a[num].push(num);
return a;
}, {})
);
It's pretty weird to have an array of identical values, though - you might consider a different data structure like
{
'141848': 3,
'142851': 2
}
to keep track of the number of occurrences of each number:
const output = files.reduce((a, file) => {
const num = file.slice(0, 6);
a[num] = (a[num] || 0) + 1;
return a;
}, {})
To obtain exactly the result you desire, you need a nested find, something like this should works:
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = files.reduce((acc, value)=> {
let n = +value.substr(0, 6); // assumes it can't be NaN
let arr = acc.find(nested => nested.find(item => item === n));
if (arr) {
arr.push(n);
} else {
acc.push([n]);
}
return acc;
}, []);
console.log(first6Array);
Notice that an hashmap instead, with the value and the number of occurrence, would be better, also in term of performance, but I don't think it mind since you have really few elements.
Also, it assumes the first six characters are actually numbers, otherwise the conversion would fail and you'll get NaN.
It would be safer adding a check to skip this scenario:
let n = +value.substr(0, 6);
if (isNaN(n)) {
return acc;
}
// etc

Array of objects: accessing an object's value without iterating through the array

I have an array of similarly structured objects:
var my_arr = [{property1: some_value, property2: another_value}, {}, {}, ...];
Currently, to find the object containing a target value, I iterate through each element of the array:
var my_obj, target_value;
for (let obj_in_arr of my_arr) {
if (obj_in_arr.property1 === target_value) {
my_obj = obj_in_arr;
break;
}
}
Is there a faster way? How can I access the object with the target value directly, without resorting to iteration?
If you prepopulate a new Map all subsequent searches will be in O(1)
const objMap = new Map()
for (let obj of my_arr) {
objMap.set(obj.property1, obj)
}
function getObject(target, map) {
return map.get(target)
}
I think you need to iterate the array anyway, but you can try _.findIndex of underscore.js
http://underscorejs.org/#findIndex
If you only need to find a value once, then iteration is really the only way.
If you will want to find many values in the array, you could create an object keyed on your target property to serve as a lookup table:
var lookup = {};
for (var i = 0; i < my_arr.length; i++) {
lookup[my_arr[i].property1] = my_arr[i];
}
That front loads some work, but could save you time ultimately if you have many lookups to make.
Lookups would be as simple as:
my_obj = lookup[target_value];
If you have access to es2015 you could make your lookup table generation a little more concise:
const lookup = my_arr.reduce((m, v) => (m[v.property1] = v, m), {});
this will still iterate through the array but you could use the native js find function.
const objArray = [{ val: 1}, { val: 2}];
const targetObj = objArray.find((obj) => obj.val == 2 ) // { val: 2}

Array within object returning length of 0, even though there are elements present

I am trying to implement a Trie in Javascript, which is easy enough but I seem to have hit a road block with my object.
The nodes are structured as follows:
var node = {
children: []
}
Children is an array of nodes that is mapped by a letter in a string. So the string "Test" would look like this:
root = {
children: [
't' => {
children: [
'e' => {
children: [
's' => {
children: [
't' => {
children: []
}
]
}
]
}
]
}
]
};
So each children array should have a length of 1, but if do something like alert(this._root.children.length); I get zero. Any thoughts on why this is happening?
Here is the rest of my implementation:
function Trie() {
this._root = {
children: []
};
}
Trie.prototype = {
//restore constructor
constructor: Trie,
add: function (str){
var curr = this._root,
prev,
currchar;
// For each character in the string
for(var i = 0, j = str.length; i < j; i++) {
// Insert only lowercase letters for efficiency
currchar = str.toLowerCase().charAt(i);
prev = curr;
curr = prev.children[currchar];
// Traverse until we hit a non-existant node
if(typeof(curr) == "undefined") {
// Make a new node
prev.children[currchar] = {
children: []
};
curr = prev.children[currchar];
}
}
}
You are adding properties to the array instance object, not elements to the array. The length property only includes array elements, not properties on the array instance object.
var a = [23, 42];
console.log(a.length); // 2
a['foo'] = 'bar';
console.log(a.length); // 2
a[2] = 1337;
console.log(a.length); // 3
EDITED:
You could instead structure the nodes like this:
var node = {
children: {},
length: function () {
var i = 0;
var k;
for (k in this.children) {
if (this.children.hasOwnProperty(k)) {
i++;
}
}
return i;
}
};
This is inefficient, of course. You should instead define a Node class with the length method on its prototype. Alternatively, define an add method that updates the length property.
I think that the problem is that you use a javasrcipt array as an associative array (as found in other languages). In javascript "associative" arrays are objects that don't have a length property. Normal arrays have numeric indices.
Irrelevant to the question but you might find this useful.
Maybe you want
str.toLowerCase().charCodeAt(i)
instead of
str.toLowerCase().charAt(i)
If str is "f1", the properties you're adding to the children array are "f" and "1" which should cause an array with property named f and length 0, and another child array with length 2 and property 1.
To get only numeric properties, you should make sure your property names are valid array indices -- positive integers representable in 31 bits.
By using charCodeAt instead of charCode, you would get the property names 102 and 49 instead of "f" and 1.

Categories