javascript filter multidimensional array - javascript

I am not familiar with javascript but I need to use it for a callback with Bokeh. I created a multidimensional array with the following content (pseduo code)
items =[
["id", Array(2898)],
["NAME", Array(2898)],
["ADDRESS", Array(2898)],
["PHONE", Array(2898)],
];
I would like to create a new array containing a subset filtered by an array of "ids"
I tried using filter and some but can't seem to get it work. here is what I got so far
let items = Object.keys(items_obj).map((key) => [key, items_obj[key]]);
let filter_items = items.filter(function(item){
return item.some(e => e['id'] === ids[0]);
Is there a simplye whay to do this? In python, I would simply filter df[df['ids'].isin([3, 6])]
Many thanks

If you want to extract a "column" of data from a matrix, you can find the column index by find the value index within the corresponding key array.
const data = [
["id", [1, 2, 3]],
["NAME", ['Bob', 'Joe', 'Nancy']],
["ADDRESS", [1, 2, 3]],
["PHONE", [1, 2, 3]]
];
const
itemsObj = Object.fromEntries(data), // Matrix to Object
itemsArr = Object.entries(itemsObj); // Object to Matrix
const getFrame = (dataFrames, key, value) => {
const [ , keyValues ] = dataFrames.find(([key]) => key === key);
const index = keyValues.indexOf(value);
return dataFrames.map(([k, v]) => [ k, v.find((w, i) => i === index) ]);
};
const
frame = getFrame(data, 'id', 2),
frameObj = Object.fromEntries(frame);
console.log(frameObj);
.as-console-wrapper { top: 0; max-height: 100% !important; }
If you want to select a range of "frames", you can modify the program as seen below:
const data = [
["id", [1, 2, 3]],
["NAME", ['Bob', 'Joe', 'Nancy']],
["ADDRESS", [1, 2, 3]],
["PHONE", [1, 2, 3]]
];
const getFrames = (dataFrames, key, values) => {
const [ , keyValues ] = dataFrames.find(([key]) => key === key);
const indicies = values.map(val => keyValues.indexOf(val)).filter(i => i > -1);
return indicies.map(index =>
dataFrames.map(([k, v]) =>
[k, v.find((x, i) => i === index)]));
};
const
frames = getFrames(data, 'id', [2, 3]),
frameObjs = frames.map(frame => Object.fromEntries(frame));
console.log(frameObjs);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Related

JavaScript how to find unique values in an array based on values in a nested array

I have a nested/multi-dimensional array like so:
[ [ 1, 1, a ], [ 1, 1 , b ], [ 2, 2, c ], [ 1 ,1, d ] ]
And I want to filter it so that it returns only unique values of the outer array based on the 1st value of each nested array.
So from the above array, it would return:
[ [1,1,a] [2,2,c] ]
Am trying to do this in vanilla javascript if possible. Thanks for any input! =)
Here is my solution.
const dedup = arr.filter((item, idx) => arr.findIndex(x => x[0] == item[0]) == idx)
It looks simple and also somehow tricky a bit.
I realize there's already three solutions, but I don't like them. My solution is
Generic - you can use unique with any selector function
O(n) - it uses a set, it doesn't run in O(n^2) time
So here it is:
/**
* #param arr - The array to get the unique values of
* #param uniqueBy - Takes the value and selects a criterion by which unique values should be taken
*
* #returns A new array containing the original values
*
* #example unique(["hello", "hElLo", "friend"], s => s.toLowerCase()) // ["hello", "friend"]
*/
function unique(arr, uniqueBy) {
const temp = new Set()
return arr.filter(v => {
const computed = uniqueBy(v)
const isContained = temp.has(computed)
temp.add(computed)
return !isContained
})
}
const arr = [ [ 1, 1, 'a' ], [ 1, 1, 'b' ], [ 2, 2, 'c' ], [ 1, 1, 'd' ] ]
console.log(unique(arr, v => v[0]))
You could filter with a set and given index.
const
uniqueByIndex = (i, s = new Set) => array => !s.has(array[i]) && s.add(array[i]),
data = [[1, 1, 'a'], [1, 1, 'b'], [2, 2, 'c'], [1, 1, 'd']],
result = data.filter(uniqueByIndex(0));
console.log(result);
const input = [[1,1,'a'], [1,1,'b'], [2,2,'c'], [1,1,'d']]
const res = input.reduce((acc, e) => acc.find(x => x[0] === e[0])
? acc
: [...acc, e], [])
console.log(res)
Create the object with keys as first element of array. Iterate over array, check if the first element of array exist in the Object, if not push into the array.
const nestedArr = [ [1,1,"a"], [1,1,"b"], [2,2,"c"], [1,1,"d"] ];
const output = {};
for(let arr of nestedArr) {
if(!output[arr[0]]) {
output[arr[0]] = arr;
}
}
console.log(Object.values(output));
Another solution, would be to maintain the count of first array element and if the count is equal to 1, then push in the final array.
const input = [ [1,1,"a"], [1,1,"b"], [2,2,"c"], [1,1,"d"] ],
count = {},
output = [];
input.forEach(arr => {
count[arr[0]] = (count[arr[0]] || 0) + 1;
if(count[arr[0]] === 1) {
output.push(arr);
}
})
console.log(output);

create a new object w/ unique IDs and specific indeces

I want a clean way to create a new object from the given data:
const groups = [1, 2, null, 1, 1, null]
here is my target:
// key = unique id, value = index of unique id in groups
const target = {
null: [2, 5],
1: [0, 3, 4],
2: [1]
}
I try to reach:
the object keys of target are the unique entries of the groups array
to get the index of each value of the unique id and save them in an own array
my current approach:
const groupIDs = [1, 2, null, 1, 1, null]
const group = {}
const uniqueIDs = [...new Set(groupIDs)]
uniqueIDs.forEach(uid => {
const arr = groupIDs.map((id, idx) => uid === id ? idx : null)
const filtered = arr.filter(idx => idx !== null)
Object.assign(group, { [uid]: filtered })
})
You could reduce the array directly by using an object as accumulator and take the values as key and the indices as value for the grouped arrays.
This approach features an logical nullish assignment ??= where the right side is assigned to the left, if the LHS is undefined or null.
const
groups = [1, 2, null, 1, 1, null],
target = groups.reduce((r, value, index) => {
r[value] ??= [];
r[value].push(index);
return r;
}, {});
console.log(target);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to create new array with key and value with 2 arrays?

I have 2 array, one for key and other for value.
Want create new array with these arrays.
key: [01, 02, 03]
value: ["hi", "hello", "welcome"]
Output I need:
[
{"key": "1","value":"hi"},
{"key": "2","value":"hello"},
{"key": "3","value":"welcome"}
]
How to get result by this way.?
My code:
output = key.map(function(obj, index){
var myObj = {};
myObj[value[index]] = obj;
return myObj;
})
Result:
[
{"1","hi"},
{"2","hello"},
{"3","welcome"}
]
const keys = [01, 02, 03];
const values = ['hi', 'hello', 'welcome'];
const res = keys.map((key, ind) => ({ 'key': ''+key, 'value': values[ind]}));
console.log(res);
There is also a proposal for the following method of Object, fromEntries, which will do exactly what you want to, but it is not supported yet by the major browsers:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
var myArray = [];
var keys = [45, 4, 9];
var cars = ["Saab", "Volvo", "BMW"];
cars.forEach(myFunction);
var txt=JSON.stringify(myArray);
document.getElementById("demo").innerHTML = txt;
function myFunction(value,index,array) {
var obj={ key : keys[index], value : value };
myArray.push(obj);
}
<p id="demo"></p>
You could take an object with arbitrary count of properties amd map new objects.
var key = [1, 2, 3],
value = ["hi", "hello", "welcome"],
result = Object
.entries({ key, value })
.reduce((r, [k, values]) => values.map((v, i) => Object.assign(
{},
r[i],
{ [k]: v }
)), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here you have another apporach using reduce():
let keys = [01, 02, 03];
let values = ['hi', 'hello', 'welcome'];
let newArray = keys.reduce((res, curr, idx) => {
res.push({'key': curr.toString(), 'value': values[idx]});
return res;
}, []);
console.log(newArray);

Javascript how to join two arrays having same property value?

How do I join arrays with the same property value? I cannot map it because it has different indexes.
var array1 = [
{'label':"label1",'position':0},
{'label':"label3",'position':2},
{'label':"label2",'position':1},
];
var array2 = [
{'label':"label1",'value':"TEXT"},
{'label':"label2",'value':"SELECT"}
];
expected output:
var array3 = [
{'label':"label1",'value':"TEXT",'position':0},
{'label':"label2",'value':"SELECT", 'position':1}
];
This is what I did, I cannot make it work,
var arr3 = arr1.map(function(v, i) {
return {
"label": v.label,
"position": v.position,
"value": arr2[?].value
}
});
I think you can use array#reduce to do something like this perhaps:
var array1 = [
{'label':"label1",'position':0},
{'label':"label3",'position':2},
{'label':"label2",'position':1},
];
var array2 = [
{'label':"label1",'value':"TEXT"},
{'label':"label2",'value':"SELECT"}
];
var array3 = array2.reduce((arr, e) => {
arr.push(Object.assign({}, e, array1.find(a => a.label == e.label)))
return arr;
}, [])
console.log(array3);
You could take a Map and check the existence for a new object.
var array1 = [{ label: "label1", position: 0 }, { label: "label3", position: 2 }, { label: "label2", position: 1 }],
array2 = [{ label: "label1", value: "TEXT" }, { label: "label2", value: "SELECT" }],
map = array1.reduce((m, o) => m.set(o.label, o), new Map),
array3 = array2.reduce((r, o) => {
if (map.has(o.label)) {
r.push(Object.assign({}, o, map.get(o.label)));
}
return r;
}, []);
console.log(array3);
.as-console-wrapper { max-height: 100% !important; top: 0; }
As per the effort, we take an assumption that array1 will be having all the labels that are in array2.
Based on that first, create a map for array2and with key being labels. Post that, filter out array1 items which have labels existing in the map and then finally merging the objects of the filtered array and its corresponding values in map extracted from array2.
var array1 = [{'label':"label1",'position':0},{'label':"label3",'position':2},{'label':"label2",'position':1}];
var array2 = [{'label':"label1",'value':"TEXT"},{'label':"label2",'value':"SELECT"}];
let map = array2.reduce((a,{label, ...rest}) => Object.assign(a,{[label]:rest}),{});
let result = array1.filter(({label}) => map[label]).map(o => ({...o, ...map[o.label]}));
console.log(result);
Also, in the above snippet, you can improve the performance further by using Array.reduce against filter and map functions to retrieve the result.
var array1 = [{'label':"label1",'position':0},{'label':"label3",'position':2},{'label':"label2",'position':1}];
var array2 = [{'label':"label1",'value':"TEXT"},{'label':"label2",'value':"SELECT"}];
let map = array2.reduce((a,{label, ...rest}) => Object.assign(a,{[label]:rest}),{});
let result = array1.reduce((a,o) => {
if(map[o.label]) a.push({...o, ...map[o.label]});
return a;
}, []);
console.log(result);
If you don't know in advance which array(s) will have their labels be a subset of the other (if any), here's a method that allows for either array1 or array2 to have labels that the other array lacks. Use reduce over array1, finding the matching label in array2 if it exists:
var array1 = [
{'label':"label1",'position':0},
{'label':"label3",'position':2},
{'label':"label2",'position':1},
];
var array2 = [
{'label':"label1",'value':"TEXT"},
{'label':"label2",'value':"SELECT"}
];
const output = array1.reduce((a, { label, position }) => {
const foundValueObj = array2.find(({ label: findLabel }) => findLabel === label);
if (!foundValueObj) return a;
const { value } = foundValueObj;
a.push({ label, value, position });
return a;
}, []);
console.log(output);
See Array.prototype.map() and Map for more info.
// Input.
const A = [{'label':"label1",'position':0},{'label':"label3",'position':2},{'label':"label2",'position':1}]
const B = [{'label':"label1",'value':"TEXT"},{'label':"label2",'value':"SELECT"}]
// Merge Union.
const mergeUnion = (A, B) => {
const mapA = new Map(A.map(x => [x.label, x]))
return B.map(y => ({...mapA.get(y.label), ...y}))
}
// Output + Proof.
const output = mergeUnion(A, B)
console.log(output)
This works.
Approach: Concatenate the objects with same label, using Object.assign()
var array1 = [{'label':"label1",'position':0},{'label':"label3",'position':2},{'label':"label2",'position':1}];
var array2 = [{'label':"label1",'value':"TEXT"},{'label':"label2",'value':"SELECT"}];
var result = [];
array2.forEach(function(value, index){
result.push(Object.assign({},array1.find(function(v,i){return v.label==value.label}),value));
});
console.log(result)
Im not good with javascript,but you could also do this
var array1 = [
{'label':"label1",'position':0},
{'label':"label3",'position':2},
{'label':"label2",'position':1},
];
var array2 = [
{'label':"label1",'value':"TEXT"},
{'label':"label2",'value':"SELECT"}
];
var array3=[];
for(var i=0;i<array1.length;i++)
{
for(var x=0;x<array2.length;x++)
{
console.log(array1[i]['label'] == array2[x]['label']);
if(array1[i]['label'] == array2[x]['label']){
array3.push({label:array1[i]['label'],value:array2[x]['value'],position:array1[i]['position']});
}
}
}
console.log(array3);

Combine several arrays into an array of objects in JavaScript

Say I have three arrays depicting some names, number of books read and how awesome these people [in names] are:
let names = ["Mary", "Joe", "Kenan"];
let numberOfBooks = [2, 1, 4];
let awesomenessLevel = ["pretty cool", "meh", "super-reader"];
I'm trying to use .reduce() to bring them together to create an array of objects containing the relevant index in each array, but I am failing miserably:
let people = [
{
name: "Mary",
noOfBooks: 2,
awesomeness: "pretty cool"
},
{
name: "Joe",
noOfBooks: 1,
awesomeness: "meh"
},
{
name: "Kenan",
noOfBooks: 4,
awesomeness: "super-reader"
}
]
I got it with reduce as well:
let arrFinal = [];
names.reduce(function(all, item, index) {
arrFinal.push({
name: item,
noOfBooks: numberOfBooks[index],
awesomeness: awesomenessLevel[index]
})
}, []);
You could do it with map, like this:
let result = names.map( (v, i) => ({
name: names[i],
noOfBooks: numberOfBooks[i],
awesomenessLevel: awesomenessLevel[i]
}));
let names = ["Mary", "Joe", "Kenan"];
let numberOfBooks = [2, 1, 4];
let awesomenessLevel = ["pretty cool", "meh", "super-reader"];
let result = names.map( (v, i) => ({
name: names[i],
noOfBooks: numberOfBooks[i],
awesomenessLevel: awesomenessLevel[i]
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
map works better than reduce in this case, because the number of elements you have in the names array (or any of the two others) is the same as the number of elements you need in the output. In that case it is more natural to use map.
Use map to create a 1-to-1 mapping between the input arrays and the output arrays.
let people = names.map(function (e, i) {
return {name:e, noOfBooks:numberOfBooks[i],awesomeness: awesomenessLevel[i]};
});
let names = ["Mary", "Joe", "Kenan"];
let numberOfBooks = [2, 1, 4];
let awesomenessLevel = ["pretty cool", "meh", "super-reader"];
let people = names.map(function (e, i) {
return {name:e, noOfBooks:numberOfBooks[i],awesomeness: awesomenessLevel[i]};
});
console.log(people);
You could use a dynamic approach by combining all arrays to one object and use the key names as property names for the result objects in the array
let names = ["Mary", "Joe", "Kenan"],
numberOfBooks = [2, 1, 4],
awesomenessLevel = ["pretty cool", "meh", "super-reader"],
object = { name: names, noOfBooks: numberOfBooks, awesomeness: awesomenessLevel },
result = Object.keys(object).reduce((r, k) =>
(object[k].forEach((a, i) =>
(r[i] = r[i] || {})[k] = a), r), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories