Use array as keys for nested array? - javascript

I have a nested array like shown below.
var arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
Is there a way to use another, un-nested array as the key(s) for arr1?
var arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
var arr2 = [3, 0];
var arr3 = [4, 1];
console.log(arr1[arr2]); // should return 'c' like arr1[3][0]
console.log(arr1[arr3]); // instead of arr1[4][1]; should return 'f'
Is there a function that allows me to do this?

You can make your own function that implements this behavior like so:
function nested_access(arr1, arr2) {
let ret = arr1;
arr2.forEach((index) => {
ret = ret[index];
});
return ret;
}
const arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
const arr2 = [2, 0];
const arr3 = [3, 1];
console.log(nested_access(arr1, arr2)); // should return 'c'
console.log(nested_access(arr1, arr3)); // should return 'f'

Just a simple method with reduce to walk the tree. (I changed your example indexes since they were off)
const lookUp = (arr, indexes) =>
indexes.reduce(
(acc, index) => acc[index],
arr);
var arr1 = ['a', 'b', ['c', 'd'],
['e', 'f']
];
var arr2 = [2, 0];
var arr3 = [3, 1];
console.log(lookUp(arr1, arr2));
console.log(lookUp(arr1, arr3));

You can find the value by iteratively accessing properties using an array of property accessors:
function findValue (obj, propertyAccessors) {
try {
let result = obj;
for (const key of propertyAccessors) result = result[key];
return result;
}
catch {
return undefined;
}
}
const arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
const arr2 = [2, 0];
const arr3 = [3, 1];
console.log(findValue(arr1, arr2)); // "c"
console.log(findValue(arr1, arr3)); // "f"
console.log(findValue(arr1, [2, 2, 3])); // undefined
If you attempt to access a property which doesn't exist, the result will be undefined. If you continue to attempt to access another property on undefined, an exception will be thrown. By using try...catch, you can catch such an error and return undefined.

Try this:
function getValue(arr, arrKeys)
{
return arrKeys.reduce(
(acum, current) => acum?.[current]
, arr
)
}
var arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
var arr2 = [2, 0];
var arr3 = [3, 1];
console.log(getValue(arr1, arr2))
console.log(getValue(arr1, arr3))

You can create an array method Array#getNested as follows:
Array.prototype.getNested = function(index) {
let out = this;
index.forEach(i => out = out[i]);
return out;
}
const arr1 = ['a', 'b', ['c', 'd'], ['e', 'f'], ['g',['h',['i', 'j']]]];
const arr2 = [2, 0];
const arr3 = [3, 1];
const arr4 = [4, 1, 1, 0];
console.log( arr1.getNested(arr2) );
console.log( arr1.getNested(arr3) );
console.log( arr1.getNested(arr4) );

Related

Given a list of links, find all link chains with length greater than one

Given an table of links of the form:
const links = [
['a', 'b'],
['c', 'd'],
['e', 'f'],
['b', 'x'],
['x', 'z']
];
where the first column is unique i.e. links[0...length][0] are all unique.
I would like to find all connections greater than 1. In the example above, the output should be
[["a", "b"], ["b", "x"], ["x", "z"]]
Here is my attempt which is based on a similar question for java
const links = [
['a', 'b'],
['c', 'd'],
['e', 'f'],
['b', 'x'],
['x', 'z']
];
connections = [];
const map = new Map()
const recurse = (value, key) => {
if (map.has(value)) {
if (key !== undefined) {
connections.push([key, map.get(key)]);
}
connections.push([value, map.get(value)])
recurse(map.get(value))
}
}
const findConnectionsWithMap = arr => {
arr.forEach(value => map.set(value[0], value[1]))
const keySet = map.keys();
//console.log(keySet);
while (key = keySet.next().value) {
recurse(map.get(key), key);
}
console.log(connections);
}
findConnectionsWithMap(links);
Current output is
[["a", "b"], ["b", "x"], ["x", "z"], ["b", "x"], ["x", "z"]]
I am not sure why the output is having duplicates but I am guessing it has to do with the recursion point.
Why is this happening?
and is this the best approach for a large record set?
You could use recursion and call the recursive method as long as the link is found between two elements. To keep chains unique you also have to remove the element once the element belongs to some chain otherwise for your data example you will actually have two chains [["a","b"],["b","x"],["x","z"]] and [["b","x"],["x","z"]]
const links = [
['a', 'b'],
['c', 'd'],
['e', 'f'],
['b', 'x'],
['x', 'z']
];
function getConnections(data) {
function connect(a, data) {
const chain = []
const index = data.findIndex((b) => a[1] === b[0])
if (index > -1) {
chain.push(data[index], ...connect(data[index], data))
data.splice(index, 1)
}
return chain
}
return data.reduce((r, e) => {
const chain = connect(e, data)
if (chain.length) {
r.push([e, ...chain])
}
return r
}, [])
}
console.log(JSON.stringify(getConnections(links)))
Based on my recent answer of detecting cyclical paths in array (much like this one), here's the solution.
This one is without recursion, just looping on all items, finding chains. this assumes only 1 -1 links. If that is not the case do let know in the comments.
const links = [
['a', 'b'],
['c', 'd'],
['e', 'f'],
['b', 'x'],
['x', 'z']
];
function search_id(arr, id) {
return arr.find(item => item[0] === id)
}
function find_paths(arr) {
var result = []
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
var seen = [item[0]]
while (true) {
var target = search_id(arr, item[1]);
if (target === undefined) {
break;
}
if (seen.indexOf(target[0]) > -1) {
// cyclical
console.error("cyclical chain found ")
break;
}
seen.push(target[0]);
item = target;
}
if (seen.length > 1) {
result.push(seen)
}
}
return result;
}
console.log(find_paths(links));

creating an multidimensional Object dynamically from an multidimensional array

Im trying to create an multidimensional Object like this one:
{ A : {a1: {},a2:{}}, B: {b1:{},b2:{}}}
from an multidimensional array like this one:
let array1 = [
['A', 'a1'],
['A', 'a1'],
['A', 'a2'],
['B', 'b1'],
['B', 'b1'],
['B', 'b2'],
];
I'm trying this for some hours now and was also reading plenty of entrys here on stackoverflow, but nothing really fits this specific case.
What i did so far:
let array1 = [
['A', 'a1'],
['A', 'a1'],
['A', 'a2'],
['B', 'b1'],
['B', 'b1'],
['B', 'b2'],
];
let object1 = {};
array1.forEach(function (subArray) {
let level1 = subArray[0];
let level2 = subArray[1];
object1[[level1]] = { ...{ [level2]: {} } };
});
console.log('object: ', object1);
//desired output: object = { A : {a1: {},a2:{}}, B: {b1:{},b2:{}}}
//what I get: object = { A : {a2:{}}, B: {b2:{}}}
So somehow in my code the entrys like {a1: {}} are getting overwritten in each iteration instead of adding a new entry.
Thanks a lot in advance.
You can use Array.reduce() along with some destructuring to get the required object output.
For each key, value pair in the original array, we add a property to the final object, (e.g. 'A'), then add a new object for each value in the array (e.g. 'a1', 'a2').
let array1 = [ ['A', 'a1'], ['A', 'a1'], ['A', 'a2'], ['B', 'b1'], ['B', 'b1'], ['B', 'b2'], ];
let output = array1.reduce((acc, [k,v]) => {
acc[k] = { ...(acc[k] || {}), [v]: {} };
return acc;
}, {})
console.log(output)
.as-console-wrapper { max-height: 100% !important; top: 0; }
One could also do this in a one-liner, but I think it's a lot harder to read (and understand):
let array1 = [ ['A', 'a1'], ['A', 'a1'], ['A', 'a2'], ['B', 'b1'], ['B', 'b1'], ['B', 'b2'], ];
let output = array1.reduce((acc, [k,v]) => ({ ...acc, [k]: { ...(acc[k] || {}), [v]: {} } }), {});
console.log(output)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Keep previous properties using ...object1[[level1]]:
let array1 = [
['A', 'a1'],
['A', 'a1'],
['A', 'a2'],
['B', 'b1'],
['B', 'b1'],
['B', 'b2'],
];
let object1 = {};
array1.forEach(function (subArray) {
let level1 = subArray[0];
let level2 = subArray[1];
object1[[level1]] = {
...object1[[level1]], // Keep previous properties
...{ [level2]: {} } // Add new
};
});
console.log('object: ', object1);

Combine unique items of an array of arrays while summing values - JS / lodash

I have an array that looks something like this:
currArray =
[
['a', 2],
['b', 3],
['c', 5],
['a', 2],
['b', 4],
['d', 6]
]
I am trying to combine the arrays that have the same value at [0], while adding the values at [1]. So the output would look like:
newArray =
[
['a', 4],
['b', 7],
['c', 5],
['d', 6]
]
Currently trying this via Vanilla JavaScript and lodash.
Any advice is much appreciated.
You can use Array.reduce():
const currArray = [
['a', 2],
['b', 3],
['c', 5],
['a', 2],
['b', 4],
['d', 6]
];
const result = currArray.reduce((res, [key, val]) => {
const correspondingArr = res.find(arr => arr[0] === key);
if (correspondingArr) {
correspondingArr[1] += val;
} else {
res.push([key, val]);
}
return res;
}, []);
console.log(result);
We can use hashmaps to store the sum, and then map the hashmap to array.
currArray =
[
['a', 2],
['b', 3],
['c', 5],
['a', 2],
['b', 4],
['d', 6]
]
// create a hash map
const currArrInfo = {};
// fill the hash map
currArray.forEach((miniArray) => {
currArrInfo[miniArray[0]] = currArrInfo[miniArray[0]] || 0;
currArrInfo[miniArray[0]]+=currArrInfo[miniArray[1]];
});
// map the hash map to array
currArray = Object.keys(currArrInfo).map((key) => [key, currArrInfo[key]]);
You can use _.groupBy() with _.head() to group all entries with the same 1st item. Then map the groups, and sum (with _.sumBy() and _.last()) the 2nd elements of each group:
const currArray = [["a",2],["b",3],["c",5],["a",2],["b",4],["d",6]]
const result = _.map(
_.groupBy(currArray, _.head), // group by the 1st item
(group, key) => [key, _.sumBy(group, _.last)] // take the key from each group, and sum all the 2nd items of each group
)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
With lodash/fp you can create a function with _.flow() that groups the items by the 1st element, sum the 2nd elements of each group, and convert to entries:
const { flow, groupBy, head, mapValues, sumBy, last, toPairs } = _
const fn = flow(
groupBy(head), // group by the 1st item
mapValues(sumBy(last)), // take the key from each group, and sum all the 2nd items of each group
toPairs // convert to entries
)
const currArray = [["a",2],["b",3],["c",5],["a",2],["b",4],["d",6]]
const result = fn(currArray)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
You could use a Map to keep a running tally for each differing currArray[0]:
currArray =
[
['a', 2],
['b', 3],
['c', 5],
['a', 2],
['b', 4],
['d', 6]
];
let map = new Map();
currArray.forEach(function(subAry)
{
let runningTally = map.get(subAry[0]);
if (runningTally)
{
map.set(subAry[0],runningTally + subAry[1]);
}
else
{
map.set(subAry[0],subAry[1]);
}
});
let newArray = Array.from(map);
console.log(newArray);

Get unique values from JSON with out looping

I am having a array in the format:
const resultData = [
[1, 2, 'a', 'b'],
[1, 4, 'c', 'f'],
[2, 5, 'a', 'b'],
[1, 2, 'c', 'd'],
[9, 3, 'c', 'f'],
[5, 4, 'f', 'g']
]
and I am trying to convert in in the format:
[{
value: "a,b",
data: [
[1, 2],
[2, 5]
]
}, {
value: "c,f",
data: [
[1, 4],
[9, 3]
]
}, {
value: "c,d",
data: [
[1, 2]
]
}, {
value: "f,g",
data: [
[5, 4]
]
}]
I am using a map currently with a for loop:
var mapp = new Map;
_.each(resultData, item => {
var x = item.col.slice(2);
if (mapp.has(x.toString())) {
var temp = mapp.get(x.toString());
temp.push([item.col[0], item.col[1]]);
mapp.set(x.toString(), temp);
} else {
var valuesArray = [];
valuesArray.push([item.col[0], item.col[1]])
mapp.set(x.toString(), valuesArray);
}
});
I am having a huge data set. Is there a possible way to do it without a loop or any other method?
You can make use of reduce and Object.values method like below
const resultData = [[1,2,'a','b'],[1,4,'c','f'],[2,5,'a','b'],[1,2,'c','d'],[9,3,'c','f'],[5,4,'f','g']]
const res = resultData.reduce((acc, item) => {
const key = item.slice(2).join();
const value = item.slice(0, 2);
if(!acc[key]) {
acc[key] = {value: key, data: [value]};
} else {
acc[key] = {...acc[key], data: [...acc[key].data, value]}
}
return acc;
}, {});
console.log(Object.values(res));
I would use reduce to generate the results:
check if the key values already exists
If it does, append the new items to the data array
If it does not, create the new object with the data
const resultData = [
[1, 2, 'a', 'b'],
[1, 4, 'c', 'f'],
[2, 5, 'a', 'b'],
[1, 2, 'c', 'd'],
[9, 3, 'c', 'f'],
[5, 4, 'f', 'g']
]
let result = resultData.reduce((arr, itm) => {
let value = itm[2] + ',' + itm[3]
let item = arr.find(i => i.value == value)
if (!item) arr.push({ value, data: [[itm[0], itm[1]]] })
else item.data.push([itm[0], itm[1]])
return arr
}, [])
console.log(result)
Javascript arrays have a .map method:
var newArray = resultData.map(function(arrayMember){
var objectToReturn = {};
objectToReturn['value'] = arrayMember; // add other transformations
return objectToReturn;
}); // map
another solution:
const res = _.chain(resultData)
.groupBy(item => _.takeRight(item, 2).join(','))
.mapValues((items, value) => ({
value,
data: _.map(items, arr => _.take(arr, 2))
}))
.values()
.value();

Create object from two arrays

How can I create an object from two arrays without using loops in javascript.
example:
array1 = [1,2,3,4,5];
array2 = [A,B,C,D,E];
I want from below object
obj = {
'1': 'A',
'2': 'B',
'3': 'C',
'4': 'D',
'5': 'E',
}
Thanks in advance
var obj = {}
array1 = [1, 2, 3, 4, 5];
array2 = ['A', 'B', 'C', 'D', 'E'];
array1.forEach(function(value, index) {
obj[value] = array2[index];
});
console.log(obj);
Try to use $.each() to iterate over one of that array and construct the object as per your requirement,
var array1 = [1,2,3,4,5],array2 = ['A','B','C','D','E'];
var obj = {};
$.each(array2,function(i,val){
obj[array1[i]] = val;
});
DEMO
An ES6, array reduce solution.
const array1 = [1, 2, 3, 4, 5];
const array2 = ['A', 'B', 'C', 'D', 'E'];
const resultMap = array1.reduce(
(accumulator, value, index) => Object.assign(accumulator, {
[value]: array2[index],
}), {}
);
console.log(resultMap);
just for fun created something like this without using any iteration methods.
const array1 = [1,2,3,4,5];
const array2 = ['A','B','C','D','E'];
let combineKeyValueProxy = new Proxy({}, {
set: function(target, prop, value, receiver) {
target[array1[prop]] = value;
return true
}
});
const output = Object.assign(combineKeyValueProxy, array2);
console.log(output) // Proxy {1: "A", 2: "B", 3: "C", 4: "D", 5: "E"}

Categories

Resources