1

I have this first array, at the end of each item it has numerical value.

const arr = ["abc#1","def#2","z#1", "z#2"]

I have to map it with below array

const arrObj = [{
  name: 'abc'
},{
  name: 'def'
},{
  name: 'z'
}]

to populate a new property value that has boolean in array

result = [{
  name: 'abc',
  value: [true, false]
},{
  name: 'def',
  value: [false, true]
},{
  name: 'z',
  value: [true, true]
}]

I'm stuck at doing multiple iteration but failed to produce above result.

const arr = ["abc#1","def#2","z#1", "z#2"]

let arrObj = [{
  name: 'abc'
},{
  name: 'def'
},{
      name: 'z'
    }]

const raw = arr.map(o => o.split('#')[0])
const key = arr.map(o => o.split('#')[1])

arrObj = arrObj.map(o => {

  console.log('raw', raw)

  if(raw.includes(o.name)) {
    console.log('key', key)
    console.log(o.name)
  }

  return {
    ...o,
    value: []
  }
})
7
  • Post function(s) and/or a cohesive group of statements and/or expressions in JavaScript as a minimal reproducible example. Also an object with duplicate keys is invalid (ex. { value: 'z', value: [true, true] } two value: is invalid) Commented Apr 1, 2020 at 6:49
  • @zer00ne thanks, saw, that. I'm stuck :( Commented Apr 1, 2020 at 6:54
  • 1
    I'm not sure where do the boolean values in the value array come from. Could you be more specific on that? Like, why the first array is [true, false] and the second is [false, true] and the last is [true, true] Commented Apr 1, 2020 at 6:57
  • @user3106579 Shouldn't name: 'z' be included in arrObj? Commented Apr 1, 2020 at 6:58
  • @zer00ne yup. Edited. Commented Apr 1, 2020 at 7:00

3 Answers 3

1

Add the following to each Object of the Array of Objects (objArray):

objArray.forEach(object => object.value = [false, false]);
// ex. value: [false, false]

Then .split() each String of the Array of Strings (strArray) at the hash #

let subArray = string.split('#');
// ex. ["abc", "1"]

Convert the second string into a real index number. Note: this step is unnecessary if the strings were properly numbered -- ex. const strArray = ["abc#0", "def#1", ...]

let index = Number(subArray[1]) - 1;

Iterate through objArray again and set each element within sub-array value to true according to matching object.name and subArray[0] and the corresponding index number.

object.value[index] = true;

Demo

Details are also commented in demo

const strArray = ["abc#1", "def#2", "z#1", "z#2"];
let objArray = [{
  name: 'abc'
}, {
  name: 'def'
}, {
  name: 'z'
}];

/*
Assign each object in objArray defaults to:
value: [false, false]
*/
objArray.forEach(object => object.value = [false, false]);

// Uncomment line below to view in console 
/*
console.log(`~~~~~~~ objArray - initial state ~~~~~~~`);
console.log(JSON.stringify(objArray));
console.log(`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`);
console.log(`~~~~~~~ Sub-arrays from strArray ~~~~~~~`);
*/

for (let string of strArray) {
  /*
  Convert each string of strArray to a subArray:
  ex. ["abc", "1"]
  */
  let subArray = string.split('#');
  // Convert the second string into a real index number
  let index = Number(subArray[1]) - 1;

  for (let object of objArray) {
    /*
    if object name matches the first string of a subArray...
    Change the object value to true at the index previously defined
    */
    if (object.name === subArray[0]) {
      object.value[index] = true;
    }
  }
  // Uncomment line below to view in console 
  //console.log(JSON.stringify(`['${subArray[0]}', '${subArray[1]}'] --> object.value index: ${index}`));
}

console.log(`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`);
console.log(`~~~~~~~~ objArray - final state ~~~~~~~~`);
console.log(JSON.stringify(objArray));
console.log(`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`);


for...of Loops

Note: this is not directly related to answer. This is a reply to a comment below:

but you can't have index in for...ofuser3106579

Refer to this example of the MDN section on .entries() method.

const paragraphs = [...document.querySelectorAll('p')];

for (let [index, paragraph] of paragraphs.entries()) {
  if (index % 2 !== 0) {
    paragraph.style.color = 'tomato';
  }
}

paragraphs.forEach((paragraph, index) => {
  if (index % 2 === 0) {
    paragraph.style.color = 'blue';
  }
});

for (let i = 0; i < paragraphs.length; i++) {
  if (i % 2 !== 0) {
    paragraphs[i].style.backgroundColor = '#000';
  }
}
*>* {
  margin-left: 15px
}

p {
  width: max-content;
  margin-left: 30px
}

main,
h1,
section,
h2,
article,
h3 {
  margin-top: -40px: margin-bottom: -40px;
}

.as-console-wrapper {
  width: 350px;
  min-height: 100%;
  margin-left: 45%;
}
<main>
  <h1>Main</h1>
  <section>
    <h2>Section A</h2>
    <p>Paragraph aa</p>
    <p>Paragraph ab</p>
    <article>
      <h3>Article A1</h3>
      <p>Paragraph A1a</p>
      <p>Paragraph A1b</p>
      <p>Paragraph A1c</p>
    </article>
    <p>Paragraph ac</p>
    <article>
      <h3>Article A2</h3>
      <p>Paragraph A2a</p>
      <p>Paragraph A2b</p>
    </article>
    <p>Paragraph ad</p>
  </section>
  <section>
    <h2>Section B</h2>
    <p>Paragraph ba</p>
    <article>
      <h3>Article B1</h3>
      <p>Paragraph B1a</p>
      <p>Paragraph B1b</p>
      <p>Paragraph B1c</p>
      <p>Paragraph B1d</p>
    </article>
    <p>Paragraph bb</p>
    <p>Paragraph bc</p>
  </section>
</main>

Sign up to request clarification or add additional context in comments.

4 Comments

sometime using raw for loop like this solve problem better than using es6 loops
A for loop is great for generating two-dimensional arrays, but I prefer for...of loops because there is less code to write. Also the .keys(), .values(), .entries() methods, and destructuring patterns can be used for more complex situations.
but you can't have index in for...of
@user3106579 see update at the end of the answer above -- titled as: `for...of`` Loop.
0

Try this (UPDATED) solution:

const arr = ['abc#1', 'def#2', 'z#1', 'z#2', 'anotherName#4'];

const arrObj = [
  { name: 'abc' },
  { name: 'def' },
  { name: 'z' }, 
  { name: 'anotherName' }
];

// find min and max IDs
let minId;
let maxId;
arr.forEach(str => {
  const id = +str.split('#')[1];
  minId = typeof minId === 'undefined' || id < minId ? id : minId;
  maxId = typeof maxId === 'undefined' || id > maxId ? id : maxId;
});

// build array of integers staring from minId to maxId including
const arrSize = maxId - minId + 1;
const booleanArr = Array.from(Array(arrSize)).map((_, i) => minId + i);

const arrMap = arr.reduce((res, curr) => ({
  ...res,
  [curr]: 1
}), {});

const result = arrObj.map(({name}) => ({
  name,
  value: booleanArr.map(i => Boolean(arrMap[`${name}#${i}`]))
}));

console.log(result);

8 Comments

will it work if there's new key like 'anotherName#4' ?
please give an example of such new scenario, and what should it influence on
I updated my answer and removed O(N x N) version, does it fit your task?
is order of id names is important in `arrObj ?
why can't use make distinctIds variable simpler? before object.value u store it as object with the same key, like {1:1, 2:2}. what's the intention? can't the reduce return array?
|
0

You could do it this way:

const arr = ["abc#1","def#2","z#1", "z#2", "anotherName#4"]
      
const boolLength = arr.reduce((r, x) => {
  const [a, b] = x.split('#');
  return +b > r ? b : r
}, 0);

const tempObject = arr.reduce((r, x) => {

  const [a, b] = x.split('#');

  const array = r[a] || Array.from({length: boolLength}, (x, i) => false)
  array[b - 1] = true;

  return { 
   ...r,
   [a]: array
};

}, {});


const result = Object.keys(tempObject).reduce((r, x) => [
  ...r, 
  {
    name: x,
    value: tempObject[x]
  }
], []);
      
console.log(result);

First, store in boolLength the boolean array max lenght. Then store ìn tempObject an object to prevent repeating name properties. And last, in result compose the array.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.